mirror of
https://github.com/pelican-dev/panel.git
synced 2025-08-03 13:02:14 +02:00
Improve turnstile error handling (+ cleanup) (#1501)
This commit is contained in:
parent
5e8cccef19
commit
5a7c6ac6e5
@ -56,9 +56,4 @@ abstract class BaseSchema
|
|||||||
->default(env("CAPTCHA_{$id}_SECRET_KEY")),
|
->default(env("CAPTCHA_{$id}_SECRET_KEY")),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,5 @@ interface CaptchaSchemaInterface
|
|||||||
|
|
||||||
public function getIcon(): ?string;
|
public function getIcon(): ?string;
|
||||||
|
|
||||||
/**
|
public function validateResponse(?string $captchaResponse = null): void;
|
||||||
* @return array<string, string|bool>
|
|
||||||
*/
|
|
||||||
public function validateResponse(?string $captchaResponse = null): array;
|
|
||||||
|
|
||||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool;
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Extensions\Captcha\Schemas\Turnstile;
|
|||||||
|
|
||||||
use App\Extensions\Captcha\CaptchaService;
|
use App\Extensions\Captcha\CaptchaService;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Contracts\Validation\ValidationRule;
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
@ -11,10 +12,12 @@ class Rule implements ValidationRule
|
|||||||
{
|
{
|
||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
$response = App::call(fn (CaptchaService $service) => $service->getActiveSchema()->validateResponse($value));
|
try {
|
||||||
|
App::call(fn (CaptchaService $service) => $service->get('turnstile')->validateResponse($value));
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
report($exception);
|
||||||
|
|
||||||
if (!$response['success']) {
|
$fail('Captcha validation failed: ' . $exception->getMessage());
|
||||||
$fail($response['message'] ?? 'Unknown error occurred, please try again');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,9 @@ class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, string|bool>
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function validateResponse(?string $captchaResponse = null): array
|
public function validateResponse(?string $captchaResponse = null): void
|
||||||
{
|
{
|
||||||
$captchaResponse ??= request()->get('cf-turnstile-response');
|
$captchaResponse ??= request()->get('cf-turnstile-response');
|
||||||
|
|
||||||
@ -83,22 +83,33 @@ class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
|||||||
->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||||
'secret' => $secret,
|
'secret' => $secret,
|
||||||
'response' => $captchaResponse,
|
'response' => $captchaResponse,
|
||||||
]);
|
])
|
||||||
|
->json();
|
||||||
|
|
||||||
return count($response->json()) ? $response->json() : [
|
if (!$response['success']) {
|
||||||
'success' => false,
|
match ($response['error-codes'][0] ?? null) {
|
||||||
'message' => 'Unknown error occurred, please try again',
|
'missing-input-secret' => throw new Exception('The secret parameter was not passed.'),
|
||||||
];
|
'invalid-input-secret' => throw new Exception('The secret parameter was invalid, did not exist, or is a testing secret key with a non-testing response.'),
|
||||||
|
'missing-input-response' => throw new Exception('The response parameter (token) was not passed.'),
|
||||||
|
'invalid-input-response' => throw new Exception('The response parameter (token) is invalid or has expired.'),
|
||||||
|
'bad-request' => throw new Exception('The request was rejected because it was malformed.'),
|
||||||
|
'timeout-or-duplicate' => throw new Exception('The response parameter (token) has already been validated before.'),
|
||||||
|
default => throw new Exception('An internal error happened while validating the response.'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->verifyDomain($response['hostname'] ?? '')) {
|
||||||
|
throw new Exception('Domain verification failed.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool
|
private function verifyDomain(string $hostname): bool
|
||||||
{
|
{
|
||||||
if (!env('CAPTCHA_TURNSTILE_VERIFY_DOMAIN', true)) {
|
if (!env('CAPTCHA_TURNSTILE_VERIFY_DOMAIN', true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$requestUrl ??= request()->url;
|
$requestUrl = parse_url(request()->url());
|
||||||
$requestUrl = parse_url($requestUrl);
|
|
||||||
|
|
||||||
return $hostname === array_get($requestUrl, 'host');
|
return $hostname === array_get($requestUrl, 'host');
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use App\Extensions\Captcha\CaptchaService;
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Foundation\Application;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use App\Events\Auth\FailedCaptcha;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
||||||
|
|
||||||
readonly class VerifyCaptcha
|
|
||||||
{
|
|
||||||
public function __construct(private Application $app) {}
|
|
||||||
|
|
||||||
public function handle(Request $request, Closure $next, CaptchaService $captchaService): mixed
|
|
||||||
{
|
|
||||||
if ($this->app->isLocal()) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
$schemas = $captchaService->getActiveSchemas();
|
|
||||||
foreach ($schemas as $schema) {
|
|
||||||
$response = $schema->validateResponse();
|
|
||||||
|
|
||||||
if ($response['success'] && $schema->verifyDomain($response['hostname'] ?? '', $request->url())) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
event(new FailedCaptcha($request->ip(), $response['message'] ?? null));
|
|
||||||
|
|
||||||
throw new HttpException(Response::HTTP_BAD_REQUEST, "Failed to validate {$schema->getId()} captcha data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// No captcha enabled
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,7 +44,6 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
'node.maintenance' => \App\Http\Middleware\MaintenanceMiddleware::class,
|
'node.maintenance' => \App\Http\Middleware\MaintenanceMiddleware::class,
|
||||||
'captcha' => \App\Http\Middleware\VerifyCaptcha::class,
|
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
->withSingletons([
|
->withSingletons([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user