Firmen-Scope (Fundament): - PM-Zugriff war hart an user_id (Autor) gebunden. Jetzt additiv: Autor ODER Mitglied der zugeordneten Firma (Owner via owner_user_id oder company_user- Pivot). Geändert in PressReleasePolicy (canManage) sowie den Queries der Listen-, Show- und Edit-Komponenten. Helfer User::accessibleCompanyIds()/ canAccessCompany(). Solo-Owner unverändert; Firmenmitglieder sehen/bearbeiten alle PMs ihrer Firma. Magic-Link-Zugang für Pressekontakte (ContactAccessService): - Öffentliches, enumeration-sicheres Formular (/pressekontakt-zugang) mit Honeypot + Rate-Limit. Eine hinterlegte Kontakt-E-Mail führt zu einem lazy angelegten, de-duplizierten customer-Account (aktiv, verifiziert über den Magic-Link-Kanal), der den Firmen seiner Kontakte als Mitglied zugeordnet wird. Versand über den bestehenden Login-Magic-Link (Generator + Consume wiederverwendet) – keine Schema-Änderung, kein paralleles System. - Dezenter Einstiegslink von der Login-Seite (PM-Frontend-Wiring später). Tests: PressReleaseCompanyScopeTest (3), ContactAccessTest (6, inkl. De-Dup, Enumeration-Sicherheit, Honeypot). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
80 lines
2.8 KiB
PHP
80 lines
2.8 KiB
PHP
<?php
|
|
|
|
use App\Enums\Portal;
|
|
use App\Models\Company;
|
|
use App\Models\PressRelease;
|
|
use App\Models\User;
|
|
use Database\Seeders\RolesAndPermissionsSeeder;
|
|
use Tests\TestCase;
|
|
|
|
beforeEach(function () {
|
|
$this->seed(RolesAndPermissionsSeeder::class);
|
|
});
|
|
|
|
test('a company member can view and manage press releases authored by a colleague', function () {
|
|
/** @var TestCase $this */
|
|
$owner = User::factory()->create(['is_active' => true]);
|
|
$owner->assignRole('customer');
|
|
|
|
$member = User::factory()->create(['is_active' => true]);
|
|
$member->assignRole('customer');
|
|
|
|
$company = Company::factory()->presseecho()->create(['owner_user_id' => $owner->id]);
|
|
$member->companies()->attach($company->id, ['role' => 'member']);
|
|
|
|
$pressRelease = PressRelease::factory()->forPortal(Portal::Presseecho)->create([
|
|
'user_id' => $owner->id,
|
|
'company_id' => $company->id,
|
|
'status' => 'draft',
|
|
]);
|
|
|
|
// Firmen-Scope: Mitglied darf, obwohl nicht Autor.
|
|
expect($member->can('view', $pressRelease))->toBeTrue();
|
|
expect($member->can('update', $pressRelease))->toBeTrue();
|
|
expect($member->can('submitForReview', $pressRelease))->toBeTrue();
|
|
expect($member->accessibleCompanyIds())->toContain($company->id);
|
|
});
|
|
|
|
test('a user outside the company still cannot access its press releases', function () {
|
|
/** @var TestCase $this */
|
|
$owner = User::factory()->create(['is_active' => true]);
|
|
$owner->assignRole('customer');
|
|
|
|
$outsider = User::factory()->create(['is_active' => true]);
|
|
$outsider->assignRole('customer');
|
|
|
|
$company = Company::factory()->presseecho()->create(['owner_user_id' => $owner->id]);
|
|
|
|
$pressRelease = PressRelease::factory()->forPortal(Portal::Presseecho)->create([
|
|
'user_id' => $owner->id,
|
|
'company_id' => $company->id,
|
|
'status' => 'draft',
|
|
]);
|
|
|
|
expect($outsider->can('view', $pressRelease))->toBeFalse();
|
|
expect($outsider->can('update', $pressRelease))->toBeFalse();
|
|
});
|
|
|
|
test('the me press release detail route resolves for a company member', function () {
|
|
/** @var TestCase $this */
|
|
$owner = User::factory()->create(['is_active' => true]);
|
|
$owner->assignRole('customer');
|
|
|
|
$member = User::factory()->create(['is_active' => true]);
|
|
$member->assignRole('customer');
|
|
|
|
$company = Company::factory()->presseecho()->create(['owner_user_id' => $owner->id]);
|
|
$member->companies()->attach($company->id, ['role' => 'member']);
|
|
|
|
$pressRelease = PressRelease::factory()->forPortal(Portal::Presseecho)->create([
|
|
'user_id' => $owner->id,
|
|
'company_id' => $company->id,
|
|
'title' => 'Firmen-PM eines Kollegen',
|
|
'status' => 'draft',
|
|
]);
|
|
|
|
$this->actingAs($member)
|
|
->get(route('me.press-releases.show', $pressRelease->id))
|
|
->assertSuccessful()
|
|
->assertSee('Firmen-PM eines Kollegen');
|
|
});
|