CluPilotCloud/app/Livewire/Concerns/ResolvesOperator.php

53 lines
2.0 KiB
PHP

<?php
namespace App\Livewire\Concerns;
use App\Models\Operator;
use Illuminate\Support\Facades\Auth;
/**
* The one place a console Livewire component resolves "who is signed in"
* and decides what happens when the answer is nobody.
*
* A Livewire action reaches its component through its own request to
* /livewire/update, separate from the request that first rendered the page —
* a modal especially, reachable without the page's own route middleware (see
* R20 in CLAUDE.md). An operator whose session has ended by the time an
* action runs still reaches this code, and Auth::guard('operator')->user()
* comes back null. Four components each dereferenced it unguarded
* ($operator->password, ->id, ->two_factor_recovery_codes, …), turning that
* into a 500 instead of a sign-in prompt.
*
* currentOperator() cannot throw its way out of the null case. Livewire only
* turns $this->redirect() into a browser navigation once the calling action
* returns NORMALLY: the redirect is queued as component state and picked up
* during Livewire's own dehydrate step (Livewire\Features\SupportRedirects),
* which runs strictly after the called method returns — an uncaught
* exception during the call skips it entirely and Livewire ships back a bare
* error response instead (confirmed by reading
* Livewire\Mechanisms\HandleComponents::update() and
* Livewire\Mechanisms\HandleRequests::handleUpdate(), which catches nothing
* but \TypeError). So this returns null and every caller checks for it:
*
* if (! $operator = $this->currentOperator()) {
* return;
* }
*
* That one-line guard is the only thing left for a caller to get wrong; the
* redirect target and the mechanism live here once, so the four cannot drift
* apart the way the original unguarded dereferences did.
*/
trait ResolvesOperator
{
protected function currentOperator(): ?Operator
{
$operator = Auth::guard('operator')->user();
if ($operator === null) {
$this->redirect(route('admin.login'));
}
return $operator;
}
}