diff --git a/api/public/index.php b/api/public/index.php index 1a7b1ae..9fef4d0 100644 --- a/api/public/index.php +++ b/api/public/index.php @@ -10,6 +10,7 @@ use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use App\Controllers\CaptchaController; use App\Controllers\LoginController; +use App\Controllers\HandoffController; use App\Services\LxdService; use Zounar\PHPProxy\Proxy; use App\Utils\LogWriterHelper; @@ -71,7 +72,7 @@ $app->group('/api', function ($group) { $group->get('/captcha', [CaptchaController::class, 'get']); $group->post('/login', [LoginController::class, 'index']); $group->get('/status', [LoginController::class, 'status']); - + $group->get('/handoff/post', [HandoffController::class, 'post']); }); /** diff --git a/api/src/Controllers/HandoffController.php b/api/src/Controllers/HandoffController.php new file mode 100644 index 0000000..744f69a --- /dev/null +++ b/api/src/Controllers/HandoffController.php @@ -0,0 +1,85 @@ +getQueryParams(); + $name = (string)($q['name'] ?? ''); + $id = (string)($q['handoff'] ?? ''); + $path = (string)($q['path'] ?? '/login'); + + if (!$name || !$id) { + return $this->html($res, 400, '

Bad request

'); + } + + $lxd = new LxdService(); + $ip = $lxd->getContainerIP($name); + if (!$ip) { + return $this->html($res, 503, '

Container not ready

'); + } + + $key = "handoff:$name:$id"; + $data = $_SESSION[$key] ?? null; + unset($_SESSION[$key]); // one-time use + + if (!$data || empty($data['username']) || empty($data['password'])) { + return $this->html($res, 410, '

Handoff expired

'); + } + + // Restrict to relative paths + $path = $this->sanitizePath($path); + + // If your container has TLS, prefer https:// + $action = 'http://' . $ip . $path; + + $html = << + + Signing you in… + +
+ + +
+ + + + +HTML; + + return $this->html($res, 200, $html); + } + + private function e(string $s): string + { + return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + } + + private function html(ResponseInterface $res, int $code, string $html): ResponseInterface + { + $res->getBody()->write($html); + return $res->withHeader('Content-Type', 'text/html; charset=utf-8')->withStatus($code); + } + + private function sanitizePath(string $raw): string + { + if (preg_match('#^https?://#i', $raw)) return '/login'; + if (!str_starts_with($raw, '/')) return '/login'; + $path = parse_url($raw, PHP_URL_PATH) ?? '/login'; + $allow = ['/', '/login', '/signin']; + return in_array($path, $allow, true) ? $path : '/login'; + } +} + \ No newline at end of file diff --git a/app/pages/index.vue b/app/pages/index.vue index 9ad7fd2..61eed19 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -2,39 +2,41 @@
- -
+
- +