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>
94 lines
2.6 KiB
PHP
94 lines
2.6 KiB
PHP
<?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;
|
||
}
|
||
}
|