api: Newsletter-Anmeldung über die API eingestellt (410)
Der Endpunkt legte Abos ohne Bestätigungsweg an, die Spezifikation versprach ein Double-Opt-in; seit dem Go-Live kein Aufruf. Antwortet jetzt mit 410, die Ability ist für neue Tokens nicht mehr wählbar (Roadmap 4.3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
002dc08b5c
commit
ccef83b952
6 changed files with 38 additions and 214 deletions
|
|
@ -5,6 +5,7 @@ use App\Models\Category;
|
|||
use App\Models\Company;
|
||||
use App\Models\NewsletterSubscription;
|
||||
use App\Models\User;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('legacy api keys in query parameters are not accepted but get the migration hint', function () {
|
||||
|
|
@ -51,86 +52,22 @@ test('api user can list categories with press release read ability', function ()
|
|||
]);
|
||||
});
|
||||
|
||||
test('api user can subscribe an email to newsletter', function () {
|
||||
test('the newsletter subscription endpoint is discontinued and creates nothing (Roadmap 4.3)', function () {
|
||||
/** @var TestCase $this */
|
||||
$user = User::factory()->create();
|
||||
|
||||
apiActingAs($user, ['newsletter:subscribe']);
|
||||
|
||||
$this->postJson('/api/v1/newsletter/subscribe', [
|
||||
'portal' => 'presseecho',
|
||||
'email' => 'newsletter@example.com',
|
||||
'first_name' => 'Max',
|
||||
'last_name' => 'Muster',
|
||||
])
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.email', 'newsletter@example.com')
|
||||
->assertJsonPath('data.is_confirmed', false);
|
||||
])->assertStatus(410);
|
||||
|
||||
expect(NewsletterSubscription::withoutGlobalScopes()
|
||||
->where('email', 'newsletter@example.com')
|
||||
->where('portal', 'presseecho')
|
||||
->exists())->toBeTrue();
|
||||
expect(NewsletterSubscription::withoutGlobalScopes()->where('email', 'newsletter@example.com')->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
test('resubscribing keeps an existing confirmation intact', function () {
|
||||
/** @var TestCase $this */
|
||||
$user = User::factory()->create();
|
||||
$subscription = NewsletterSubscription::withoutGlobalScopes()->create([
|
||||
'portal' => 'presseecho',
|
||||
'user_id' => $user->id,
|
||||
'email' => 'confirmed@example.com',
|
||||
'is_confirmed' => true,
|
||||
'confirmation_token' => null,
|
||||
'subscribed_at' => now()->subMonth(),
|
||||
'unsubscribed_at' => now()->subDay(),
|
||||
]);
|
||||
apiActingAs($user, ['newsletter:subscribe']);
|
||||
|
||||
$this->postJson('/api/v1/newsletter/subscribe', [
|
||||
'portal' => 'presseecho',
|
||||
'email' => 'confirmed@example.com',
|
||||
'first_name' => 'Neu',
|
||||
])->assertCreated()
|
||||
->assertJsonPath('data.is_confirmed', true);
|
||||
|
||||
$subscription->refresh();
|
||||
|
||||
expect($subscription->is_confirmed)->toBeTrue()
|
||||
->and($subscription->confirmation_token)->toBeNull()
|
||||
->and($subscription->unsubscribed_at)->toBeNull()
|
||||
->and($subscription->first_name)->toBe('Neu');
|
||||
});
|
||||
|
||||
test('an orphaned subscription without owner can be rebound instead of being permanently blocked', function () {
|
||||
/** @var TestCase $this */
|
||||
// user_id NULL entsteht z. B. nach Konto-Löschung (FK nullOnDelete) oder
|
||||
// durch Legacy-Import. Ohne Rebind wäre portal+email dauerhaft gesperrt.
|
||||
$subscription = NewsletterSubscription::withoutGlobalScopes()->create([
|
||||
'portal' => 'presseecho',
|
||||
'user_id' => null,
|
||||
'email' => 'orphan@example.com',
|
||||
'is_confirmed' => false,
|
||||
'subscribed_at' => now()->subMonth(),
|
||||
]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
apiActingAs($user, ['newsletter:subscribe']);
|
||||
|
||||
$this->postJson('/api/v1/newsletter/subscribe', [
|
||||
'portal' => 'presseecho',
|
||||
'email' => 'orphan@example.com',
|
||||
'first_name' => 'Wieder da',
|
||||
])->assertCreated();
|
||||
|
||||
expect($subscription->fresh()->user_id)->toBe($user->id)
|
||||
->and($subscription->fresh()->first_name)->toBe('Wieder da');
|
||||
});
|
||||
|
||||
test('an api user cannot take over another newsletter subscription', function () {
|
||||
test('the discontinued endpoint leaves existing subscriptions untouched', function () {
|
||||
/** @var TestCase $this */
|
||||
$owner = User::factory()->create();
|
||||
$attacker = User::factory()->create();
|
||||
$subscription = NewsletterSubscription::withoutGlobalScopes()->create([
|
||||
'portal' => 'presseecho',
|
||||
'user_id' => $owner->id,
|
||||
|
|
@ -139,15 +76,22 @@ test('an api user cannot take over another newsletter subscription', function ()
|
|||
'is_confirmed' => true,
|
||||
'subscribed_at' => now(),
|
||||
]);
|
||||
apiActingAs($attacker, ['newsletter:subscribe']);
|
||||
apiActingAs(User::factory()->create(), ['newsletter:subscribe']);
|
||||
|
||||
$this->postJson('/api/v1/newsletter/subscribe', [
|
||||
'portal' => 'presseecho',
|
||||
'email' => 'owned@example.com',
|
||||
'first_name' => 'Übernommen',
|
||||
])->assertConflict();
|
||||
])->assertStatus(410);
|
||||
|
||||
expect($subscription->fresh()->user_id)->toBe($owner->id)
|
||||
->and($subscription->fresh()->first_name)->toBe('Original')
|
||||
->and($subscription->fresh()->is_confirmed)->toBeTrue();
|
||||
->and($subscription->fresh()->first_name)->toBe('Original');
|
||||
});
|
||||
|
||||
test('new api tokens can no longer request the newsletter ability', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->actingAs(User::factory()->create(['is_active' => true]));
|
||||
|
||||
Livewire::test('customer.tokens')
|
||||
->assertDontSee('Newsletter-Anmeldungen auslösen');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue