fix(setup): source the console host from the panel hostname, not op.console

The owner setup URL and the limbo login link were built from the admin host
(op.console.<root>, with an op.console.localhost fallback) and a hardcoded
console.<root>, so an operator who set a custom panel_hostname got an unreachable setup
link and a wrong login target. Thread the resolved panel host (defaultPanelHostname)
through performSetupMCBind, the MC-bind TUI, and the login system-server env
(new FELIS_PANEL_HOSTNAME); the limbo plugin prefers it and keeps console.<root> only as
the fallback for an older operator whose env predates it. This also matters for security:
the only wired WebAuthn verifier is scoped to the panel host, so passkey enrollment must
land on the panel face, never op.console.

While here, the limbo login handler checks link status before minting a bind code: an
already-linked player is sent straight to the lobby instead of being shown a useless code.
This commit is contained in:
flyemoji committed 2026-07-16 13:27:02 +09:00
1 parent b5cd4501e5
commit 9ea35304e3
10 files changed
+78 -52

No files matched your search

@@ -196,14 +196,22 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
return;
}
// The console host the player links at. Prefer the resolved FELIS_PANEL_HOSTNAME
// the provisioner bakes in (single source of truth — it honours a custom
// panel_hostname); fall back to console.<root> only for an older operator whose
// env predates it. With neither set there is no link to build, so login stays off.
String panelHost = trimmed(System.getenv("FELIS_PANEL_HOSTNAME"));
String rootDomain = trimmed(System.getenv("FELIS_ROOT_DOMAIN"));
if (rootDomain == null) {
LOG.warning("FelisLimbo: FELIS_ROOT_DOMAIN unset — cannot build the console login link");
if (panelHost == null && rootDomain != null) {
panelHost = "console." + rootDomain;
}
if (panelHost == null) {
LOG.warning("FelisLimbo: neither FELIS_PANEL_HOSTNAME nor FELIS_ROOT_DOMAIN set — cannot build the console login link");
loginEnabled = false;
return;
}
this.consoleUrl = "https://console." + rootDomain;
this.consoleUrl = "https://" + panelHost;
String lobby = trimmed(System.getenv("FELIS_LOBBY_SERVER"));
this.lobbyServer = lobby != null ? lobby : "lobby";
this.timeoutMillis = loginTimeoutSeconds() * 1000L;
@@ -229,6 +237,14 @@ public final class FelisLimboPlugin extends LimboPlugin implements Listener {
disconnectOnMain(id, "该用户名已被回收保护 / This username is under reclaim protection. Contact staff.");
return;
}
// Check registration before minting: an already-linked player needs no
// bind code, so send them straight to the lobby instead of flashing a
// useless code. Only unlinked players get one. The on-demand /link
// command (proxy + lobby) stays the door to a fresh web session.
if (apiClient.linkStatus(id)) {
getServer().getScheduler().runTask(this, () -> transferToLobby(id));
return;
}
LinkCode code = linkClient.requestCode(id);
getServer().getScheduler().runTask(this, () -> presentAndPoll(id, code));
} catch (LinkException e) {