new funcionality
This commit is contained in:
81
app/Support/WebhookEndpoints.php
Normal file
81
app/Support/WebhookEndpoints.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Models\WebhookEndpoint;
|
||||
|
||||
/**
|
||||
* Compartido por OAuth\WebhookController (sesión) y Api\WebhookController
|
||||
* (token) — mismo patrón que AnalyticsSummary/AuditLog::paginatedFor/UserSessions.
|
||||
*/
|
||||
class WebhookEndpoints
|
||||
{
|
||||
public const EVENTS = ['token.revoked', 'user.deactivated'];
|
||||
|
||||
/**
|
||||
* Protección anti-SSRF: rechaza URLs que resuelven a IPs privadas,
|
||||
* loopback o reservadas (incluye 169.254.169.254, el endpoint de
|
||||
* metadata de instancia en AWS/GCP/Azure). Se llama tanto al
|
||||
* crear/editar un webhook como, de nuevo, justo antes de cada envío
|
||||
* (SendWebhookJob) — un dominio público al registrarse puede resolver
|
||||
* a una IP interna más tarde (DNS rebinding).
|
||||
*/
|
||||
public static function isSafeUrl(string $url): bool
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
|
||||
if (! $parts || empty($parts['host']) || ! in_array($parts['scheme'] ?? '', ['http', 'https'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$host = $parts['host'];
|
||||
|
||||
if (filter_var($host, FILTER_VALIDATE_IP)) {
|
||||
return self::isPublicIp($host);
|
||||
}
|
||||
|
||||
$records = array_merge(
|
||||
@dns_get_record($host, DNS_A) ?: [],
|
||||
@dns_get_record($host, DNS_AAAA) ?: [],
|
||||
);
|
||||
|
||||
if (empty($records)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($records as $record) {
|
||||
$ip = $record['ip'] ?? $record['ipv6'] ?? null;
|
||||
|
||||
if (! $ip || ! self::isPublicIp($ip)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function isPublicIp(string $ip): bool
|
||||
{
|
||||
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
|
||||
}
|
||||
|
||||
public static function present(WebhookEndpoint $endpoint): array
|
||||
{
|
||||
$last = $endpoint->deliveries()->latest('created_at')->first();
|
||||
|
||||
return [
|
||||
'id' => $endpoint->id,
|
||||
'client_id' => $endpoint->client_id,
|
||||
'url' => $endpoint->url,
|
||||
'events' => $endpoint->events,
|
||||
'active' => $endpoint->active,
|
||||
'created_at' => $endpoint->created_at,
|
||||
'last_delivery' => $last ? [
|
||||
'event' => $last->event,
|
||||
'response_status' => $last->response_status,
|
||||
'delivered_at' => $last->delivered_at,
|
||||
'created_at' => $last->created_at,
|
||||
] : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user