mirror of
https://github.com/nextcloud/all-in-one.git
synced 2026-06-10 16:38:18 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbafd6ff8c | |||
| adcc41f401 | |||
| 68bb93a2c8 | |||
| a415c76ad2 | |||
| 79e05f33cd | |||
| ef58220c09 | |||
| 6a9e55a8de | |||
| 8356d0dadc | |||
| 3e72f06d32 |
@@ -0,0 +1,26 @@
|
|||||||
|
// This script is loaded after a successful token-based login.
|
||||||
|
// It replaces the browser's current history entry (stripping the token from the
|
||||||
|
// URL) before navigating to the main AIO page, so the token is never left in
|
||||||
|
// the browser history and cannot be accidentally exposed via the back-button.
|
||||||
|
//
|
||||||
|
// The target URL is passed via the script tag's data-target attribute.
|
||||||
|
// document.currentScript is only available during synchronous script execution
|
||||||
|
// (not with defer/async), so this script is loaded without those attributes.
|
||||||
|
//
|
||||||
|
// We replace with location.pathname only (no query string, no hash), which
|
||||||
|
// intentionally strips the ?token=… parameter and any hash fragment from the
|
||||||
|
// recorded history entry.
|
||||||
|
|
||||||
|
// Guard against environments where document.currentScript may be null.
|
||||||
|
if (!document.currentScript) {
|
||||||
|
window.location.replace('/');
|
||||||
|
} else {
|
||||||
|
const rawTarget = document.currentScript.dataset.target;
|
||||||
|
|
||||||
|
// Only accept the exact relative path we set server-side to prevent any
|
||||||
|
// potential open-redirect via a manipulated data-target value.
|
||||||
|
const target = rawTarget === '../../' ? rawTarget : '/';
|
||||||
|
|
||||||
|
history.replaceState(null, '', location.pathname);
|
||||||
|
window.location.replace(target);
|
||||||
|
}
|
||||||
@@ -181,7 +181,7 @@ $app->get('/containers', function (Request $request, Response $response, array $
|
|||||||
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
|
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
|
||||||
'community_containers_enabled' => $configurationManager->aioCommunityContainers,
|
'community_containers_enabled' => $configurationManager->aioCommunityContainers,
|
||||||
'bypass_container_update' => $bypass_container_update,
|
'bypass_container_update' => $bypass_container_update,
|
||||||
]);
|
])->withHeader('Cache-Control', 'no-store');
|
||||||
})->setName('profile');
|
})->setName('profile');
|
||||||
$app->get('/login', function (Request $request, Response $response, array $args) use ($container) {
|
$app->get('/login', function (Request $request, Response $response, array $args) use ($container) {
|
||||||
$view = Twig::fromRequest($request);
|
$view = Twig::fromRequest($request);
|
||||||
@@ -209,7 +209,7 @@ $app->get('/setup', function (Request $request, Response $response, array $args)
|
|||||||
[
|
[
|
||||||
'password' => $setup->Setup(),
|
'password' => $setup->Setup(),
|
||||||
]
|
]
|
||||||
);
|
)->withHeader('Cache-Control', 'no-store');
|
||||||
});
|
});
|
||||||
$app->get('/log', function (Request $request, Response $response, array $args) use ($container) {
|
$app->get('/log', function (Request $request, Response $response, array $args) use ($container) {
|
||||||
$params = $request->getQueryParams();
|
$params = $request->getQueryParams();
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ readonly class DockerController {
|
|||||||
|
|
||||||
$body = $nonbufResp->getBody();
|
$body = $nonbufResp->getBody();
|
||||||
$addToStreamingResponseBody = function (string $message) use ($body) : void {
|
$addToStreamingResponseBody = function (string $message) use ($body) : void {
|
||||||
$body->write("<div>$message</div>");
|
$body->write('<div>' . htmlspecialchars($message, ENT_QUOTES | ENT_HTML5) . '</div>');
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
|
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
|
||||||
@@ -430,7 +430,7 @@ readonly class DockerController {
|
|||||||
// if it'll actually pull an image), but which should not need to know anything about the
|
// if it'll actually pull an image), but which should not need to know anything about the
|
||||||
// wanted markup or formatting.
|
// wanted markup or formatting.
|
||||||
$addToStreamingResponseBody = function (Container $container, string $message) use ($nonbufResp) : void {
|
$addToStreamingResponseBody = function (Container $container, string $message) use ($nonbufResp) : void {
|
||||||
$nonbufResp->getBody()->write("<div>{$container->displayName}: {$message}</div>");
|
$nonbufResp->getBody()->write('<div>' . htmlspecialchars($container->displayName, ENT_QUOTES | ENT_HTML5) . ': ' . htmlspecialchars($message, ENT_QUOTES | ENT_HTML5) . '</div>');
|
||||||
};
|
};
|
||||||
|
|
||||||
return $addToStreamingResponseBody;
|
return $addToStreamingResponseBody;
|
||||||
|
|||||||
@@ -39,7 +39,19 @@ readonly class LoginController {
|
|||||||
$token = $request->getQueryParams()['token'] ?? '';
|
$token = $request->getQueryParams()['token'] ?? '';
|
||||||
if($this->authManager->CheckToken($token)) {
|
if($this->authManager->CheckToken($token)) {
|
||||||
$this->authManager->SetAuthState(true);
|
$this->authManager->SetAuthState(true);
|
||||||
return $response->withHeader('Location', '../..')->withStatus(302);
|
// Return a minimal HTML page that uses JavaScript to replace the browser's
|
||||||
|
// current history entry (removing the token from it) before navigating to
|
||||||
|
// the main AIO page. This prevents the token from remaining in browser history.
|
||||||
|
// The script is served from 'self'; same-origin scripts are already trusted under
|
||||||
|
// the 'script-src-elem self' CSP directive, so no SRI hash is needed here.
|
||||||
|
$response->getBody()->write(
|
||||||
|
'<!DOCTYPE html>' .
|
||||||
|
'<html lang="en">' .
|
||||||
|
'<head><script src="../../clean-history.js" data-target="../../"></script></head>' .
|
||||||
|
'<body></body>' .
|
||||||
|
'</html>'
|
||||||
|
);
|
||||||
|
return $response->withHeader('Content-Type', 'text/html; charset=utf-8')->withStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Punish failed auth attempts with a delay, as a very simple means against bots.
|
// Punish failed auth attempts with a delay, as a very simple means against bots.
|
||||||
|
|||||||
Reference in New Issue
Block a user