Files

62 lines
2.5 KiB
PHP
Raw Permalink Normal View History

2026-08-29 15:53:53 +03:00
<?php
namespace App\Http\Controllers;
use App\Http\Middleware\SetLocale;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Str;
class LocaleController extends Controller
{
public function __invoke(Request $request): RedirectResponse
{
$data = $request->validate([
'locale' => ['required', 'in:'.implode(',', SetLocale::SUPPORTED)],
'return_to' => ['nullable', 'string', 'max:2048'],
]);
$locale = $data['locale'];
$request->session()->put('locale', $locale);
Cookie::queue(cookie('locale', $locale, 60 * 24 * 365, '/', null, $request->isSecure(), true, false, 'lax'));
if ($request->user()) {
$request->user()->forceFill(['locale' => $locale])->save();
}
$target = $this->safeRelativeTarget($data['return_to'] ?? null)
?? $this->safeRefererTarget($request)
?? $this->fallbackTarget($request);
return redirect()->to($target);
}
private function safeRelativeTarget(?string $target): ?string
{
if (! is_string($target) || ! str_starts_with($target, '/') || str_starts_with($target, '//')) return null;
$decoded = $target;
for ($i = 0; $i < 3; $i++) $decoded = rawurldecode($decoded);
if (! str_starts_with($decoded, '/') || str_starts_with($decoded, '//') || Str::contains($decoded, ["\r", "\n", '\\'])) return null;
if (parse_url($decoded, PHP_URL_HOST) !== null || parse_url($decoded, PHP_URL_SCHEME) !== null) return null;
return $target;
}
private function safeRefererTarget(Request $request): ?string
{
$referer = $request->headers->get('referer');
if (! $referer) return null;
$parts = parse_url($referer);
$requestPort = $request->getPort();
$refererPort = $parts['port'] ?? (($parts['scheme'] ?? '') === 'https' ? 443 : 80);
if (($parts['scheme'] ?? null) !== $request->getScheme() || ($parts['host'] ?? null) !== $request->getHost() || $refererPort !== $requestPort) return null;
return $this->safeRelativeTarget(($parts['path'] ?? '/').(isset($parts['query']) ? '?'.$parts['query'] : ''));
}
private function fallbackTarget(Request $request): string
{
if ($request->user()?->canAccessPanel(filament()->getPanel('admin'))) return '/admin';
if ($request->user()) return route('dashboard', absolute: false);
return route('login', absolute: false);
}
}