mirror of
https://github.com/pelican-dev/panel.git
synced 2025-08-03 00:02:18 +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")),
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,5 @@ interface CaptchaSchemaInterface
|
||||
|
||||
public function getIcon(): ?string;
|
||||
|
||||
/**
|
||||
* @return array<string, string|bool>
|
||||
*/
|
||||
public function validateResponse(?string $captchaResponse = null): array;
|
||||
|
||||
public function verifyDomain(string $hostname, ?string $requestUrl = null): bool;
|
||||
public function validateResponse(?string $captchaResponse = null): void;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace App\Extensions\Captcha\Schemas\Turnstile;
|
||||
|
||||
use App\Extensions\Captcha\CaptchaService;
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
@ -11,10 +12,12 @@ class Rule implements ValidationRule
|
||||
{
|
||||
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($response['message'] ?? 'Unknown error occurred, please try again');
|
||||
$fail('Captcha validation failed: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
||||
@ -83,22 +83,33 @@ class TurnstileSchema extends BaseSchema implements CaptchaSchemaInterface
|
||||
->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||
'secret' => $secret,
|
||||
'response' => $captchaResponse,
|
||||
]);
|
||||
])
|
||||
->json();
|
||||
|
||||
return count($response->json()) ? $response->json() : [
|
||||
'success' => false,
|
||||
'message' => 'Unknown error occurred, please try again',
|
||||
];
|
||||
if (!$response['success']) {
|
||||
match ($response['error-codes'][0] ?? null) {
|
||||
'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)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$requestUrl ??= request()->url;
|
||||
$requestUrl = parse_url($requestUrl);
|
||||
$requestUrl = parse_url(request()->url());
|
||||
|
||||
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,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'node.maintenance' => \App\Http\Middleware\MaintenanceMiddleware::class,
|
||||
'captcha' => \App\Http\Middleware\VerifyCaptcha::class,
|
||||
]);
|
||||
})
|
||||
->withSingletons([
|
||||
|
Loading…
x
Reference in New Issue
Block a user