🚀 Enhance authorization and exception handling across Livewire components and SecurityMonitor

- **SecurityMonitor:** Added logic to record and prevent logging of locked-property exceptions, while ensuring non-security exceptions are properly forwarded.
- **Livewire `Members/Admin`:** Centralized authorization logic in private methods, enforced access control on actions, and moved allowed pubkeys to class constant for maintainability.
- **Livewire `News`:** Enforced authorization for editing and deleting news with guard methods and ensured unauthorized users can't access data.
- **Bootstrap exceptions:** Implemented custom exception handling to record Livewire-related security issues while preventing redundant logs.
- Updated tests with new behavior verification covering access control and exception responses.
This commit is contained in:
HolgerHatGarKeineNode
2026-06-02 19:23:51 +02:00
parent 59bc440a59
commit 5f28bfedd4
6 changed files with 251 additions and 34 deletions
+34
View File
@@ -2,7 +2,9 @@
use App\Models\SecurityAttempt;
use App\Services\SecurityMonitor;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Livewire\Features\SupportLockedProperties\CannotUpdateLockedPropertyException;
beforeEach(function () {
@@ -145,6 +147,38 @@ it('truncates long values', function () {
expect(strlen($attempt->user_agent))->toBeLessThanOrEqual(500);
});
it('records a security attempt when a locked-property exception is reported through the handler', function () {
$exception = new CannotUpdateLockedPropertyException('isLoggedIn');
app(ExceptionHandler::class)->report($exception);
expect(SecurityAttempt::count())->toBe(1)
->and(SecurityAttempt::first()->target_property)->toBe('isLoggedIn');
});
it('does not forward locked-property exceptions to the default log stack', function () {
Log::spy();
app(ExceptionHandler::class)->report(new CannotUpdateLockedPropertyException('isLoggedIn'));
expect(SecurityAttempt::count())->toBe(1);
Log::shouldNotHaveReceived('log');
Log::shouldNotHaveReceived('error');
Log::shouldNotHaveReceived('critical');
Log::shouldNotHaveReceived('warning');
});
it('still forwards non-security exceptions to the default log stack', function () {
Log::spy();
app(ExceptionHandler::class)->report(new RuntimeException('boom'));
expect(SecurityAttempt::count())->toBe(0);
Log::shouldHaveReceived('error');
});
it('handles X-Forwarded-For header', function () {
$exception = new CannotUpdateLockedPropertyException('test');