mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 22:23:39 +00:00
feat(observability): add structured audit log channel for API and webhook events
Introduce a dedicated `audit` log channel (daily rotation, configurable retention via LOG_AUDIT_DAYS) and a small `auditLog()` / `auditLogWebhookFailure()` helper used to record state-changing API operations and webhook events. Instrumented: - API mutation endpoints (create / update / delete / start / stop / restart) across applications, services, databases (incl. backups, env vars, storage), servers, projects + environments, scheduled tasks, private keys, GitHub apps, cloud provider tokens, Hetzner server provisioning, instance enable/disable. - Webhook signature verification outcomes for GitHub, GitLab, Bitbucket, Gitea and Stripe, plus the Sentinel push endpoint. - Authentication and authorization outcomes via the global exception handler and the `ApiAbility` middleware (unauthenticated, ability-denied, policy-denied). The helper is wrapped in try/catch so logging failures never affect the request path. Successful operations log at `info`; suspicious/denied requests log at `warning`. Operators wanting a failures-only feed can set `LOG_AUDIT_LEVEL=warning`. Includes a feature test suite covering the helper, the webhook providers and the new auth/authorization log paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
923525a310
commit
a2096c6f68
@@ -4,8 +4,10 @@ namespace App\Exceptions;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Psr\Log\LogLevel;
|
||||
use RuntimeException;
|
||||
use Sentry\Laravel\Integration;
|
||||
use Sentry\State\Scope;
|
||||
@@ -16,7 +18,7 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* A list of exception types with their corresponding custom log levels.
|
||||
*
|
||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||
* @var array<class-string<Throwable>, LogLevel::*>
|
||||
*/
|
||||
protected $levels = [
|
||||
//
|
||||
@@ -25,7 +27,7 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
* @var array<int, class-string<Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
ProcessException::class,
|
||||
@@ -49,6 +51,13 @@ class Handler extends ExceptionHandler
|
||||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||
if ($request->is('api/*')) {
|
||||
auditLog('api.auth.unauthenticated', [
|
||||
'reason' => $exception->getMessage(),
|
||||
'guards' => $exception->guards(),
|
||||
], 'warning');
|
||||
}
|
||||
|
||||
return response()->json(['message' => $exception->getMessage()], 401);
|
||||
}
|
||||
|
||||
@@ -61,8 +70,15 @@ class Handler extends ExceptionHandler
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
// Handle authorization exceptions for API routes
|
||||
if ($e instanceof \Illuminate\Auth\Access\AuthorizationException) {
|
||||
if ($e instanceof AuthorizationException) {
|
||||
if ($request->is('api/*') || $request->expectsJson()) {
|
||||
if ($request->is('api/*')) {
|
||||
auditLog('api.auth.policy_denied', [
|
||||
'reason' => $e->getMessage(),
|
||||
'route' => $request->route()?->getName() ?? $request->path(),
|
||||
], 'warning');
|
||||
}
|
||||
|
||||
// Get the custom message from the policy if available
|
||||
$message = $e->getMessage();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user