presseportale/app/Policies/PressReleasePolicy.php
Kevin Adametz 980763c362 WS-2: Firmen-Scope für PMs & Magic-Link-Zugang für Pressekontakte
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>
2026-06-16 08:33:12 +00:00

94 lines
2.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Policies;
use App\Enums\PressReleaseStatus;
use App\Models\PressRelease;
use App\Models\User;
class PressReleasePolicy
{
public function before(User $user): ?bool
{
return $user->is_super_admin ? true : null;
}
public function viewAny(User $user): bool
{
return $user->canAccessCustomer();
}
public function view(User $user, PressRelease $pressRelease): bool
{
if ($user->canAccessAdmin()) {
return true;
}
return $this->canManage($user, $pressRelease);
}
public function create(User $user): bool
{
return $user->canAccessCustomer();
}
public function update(User $user, PressRelease $pressRelease): bool
{
if (! $this->canManage($user, $pressRelease) && ! $user->canAccessAdmin()) {
return false;
}
return in_array(
$pressRelease->status,
[PressReleaseStatus::Draft, PressReleaseStatus::Rejected, PressReleaseStatus::Review],
true,
) || $user->canAccessAdmin();
}
public function submitForReview(User $user, PressRelease $pressRelease): bool
{
return $this->canManage($user, $pressRelease)
&& in_array($pressRelease->status, [PressReleaseStatus::Draft, PressReleaseStatus::Rejected], true);
}
public function delete(User $user, PressRelease $pressRelease): bool
{
if ($user->canAccessAdmin()) {
return true;
}
return $this->canManage($user, $pressRelease)
&& $pressRelease->status !== PressReleaseStatus::Published;
}
public function restore(User $user, PressRelease $pressRelease): bool
{
return $user->canAccessAdmin();
}
public function forceDelete(User $user, PressRelease $pressRelease): bool
{
return $user->is_super_admin === true;
}
public function publish(User $user, PressRelease $pressRelease): bool
{
return $user->canAccessAdmin() && $user->can('press-releases:publish');
}
/**
* Zugriff auf eine PM hat der Autor ODER ein Mitglied der zugeordneten
* Firma (Owner/Team-Mitglied). So sehen/bearbeiten Firmenkontakte inkl.
* der per Magic-Link lazy angelegten Accounts die PMs ihrer Firma.
*/
private function canManage(User $user, PressRelease $pressRelease): bool
{
return $this->isAuthor($user, $pressRelease)
|| $user->canAccessCompany($pressRelease->company_id);
}
private function isAuthor(User $user, PressRelease $pressRelease): bool
{
return $pressRelease->user_id === $user->id;
}
}