account: fix Wolf card unlock flow
This commit is contained in:
parent
b4373b9325
commit
1a231dd474
@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
from typing import Any
|
||||
|
||||
from flask import jsonify, request
|
||||
@ -8,10 +9,45 @@ from .. import ariadne_client
|
||||
from ..keycloak import require_auth, require_account_access
|
||||
|
||||
|
||||
def _valid_ip(value: str) -> str:
|
||||
return str(ipaddress.ip_address(value.strip()))
|
||||
|
||||
|
||||
def _public_ip_from_chain(values: list[str]) -> str:
|
||||
"""Return the nearest public client IP from a trusted proxy chain."""
|
||||
|
||||
candidates: list[str] = []
|
||||
for value in values:
|
||||
for item in value.split(","):
|
||||
item = item.strip()
|
||||
if item:
|
||||
candidates.append(item)
|
||||
|
||||
for candidate in reversed(candidates):
|
||||
try:
|
||||
ip = ipaddress.ip_address(candidate)
|
||||
except ValueError:
|
||||
continue
|
||||
if ip.is_global:
|
||||
return str(ip)
|
||||
|
||||
for candidate in reversed(candidates):
|
||||
try:
|
||||
return _valid_ip(candidate)
|
||||
except ValueError:
|
||||
continue
|
||||
return ""
|
||||
|
||||
|
||||
def _client_ip() -> str:
|
||||
# ProxyFix normalizes this from the trusted Traefik hop. Reading raw
|
||||
# forwarding headers here would let a browser request choose an unlock IP.
|
||||
return request.remote_addr or ""
|
||||
# Walk right-to-left through proxy-added headers. This ignores spoofed
|
||||
# left-most values while still finding the public IP before cluster hops.
|
||||
chain = [
|
||||
request.headers.get("X-Forwarded-For", ""),
|
||||
request.headers.get("X-Real-IP", ""),
|
||||
request.remote_addr or "",
|
||||
]
|
||||
return _public_ip_from_chain(chain)
|
||||
|
||||
|
||||
def _json_payload() -> dict[str, Any]:
|
||||
|
||||
@ -38,7 +38,7 @@ def test_wolf_status_proxies_source_ip(monkeypatch) -> None:
|
||||
resp = client.get(
|
||||
"/api/account/wolf",
|
||||
environ_base={"REMOTE_ADDR": "1.2.3.4"},
|
||||
headers={"X-Forwarded-For": "9.9.9.9, 10.0.0.1"},
|
||||
headers={"X-Forwarded-For": "9.9.9.9, 181.1.87.186, 10.0.0.1"},
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
@ -52,13 +52,27 @@ def test_wolf_unlock_uses_current_source_ip(monkeypatch) -> None:
|
||||
"/api/account/wolf/firewall/unlock",
|
||||
json={"ttl_seconds": 120},
|
||||
environ_base={"REMOTE_ADDR": "5.6.7.8"},
|
||||
headers={"X-Real-IP": "9.9.9.9"},
|
||||
headers={"X-Real-IP": "10.42.28.0"},
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert ariadne.calls == [("POST", "/api/game-stream/firewall/unlock", {"ttl_seconds": 120, "ip": "5.6.7.8"}, None)]
|
||||
|
||||
|
||||
def test_wolf_source_ip_prefers_nearest_public_proxy_value(monkeypatch) -> None:
|
||||
client, ariadne = make_client(monkeypatch)
|
||||
|
||||
resp = client.post(
|
||||
"/api/account/wolf/firewall/unlock",
|
||||
json={},
|
||||
environ_base={"REMOTE_ADDR": "10.42.28.0"},
|
||||
headers={"X-Forwarded-For": "9.9.9.9, 181.1.87.186, 10.42.28.0"},
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert ariadne.calls == [("POST", "/api/game-stream/firewall/unlock", {"ip": "181.1.87.186"}, None)]
|
||||
|
||||
|
||||
def test_wolf_pairing_and_admin_actions_proxy(monkeypatch) -> None:
|
||||
client, ariadne = make_client(monkeypatch)
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@ export async function initAuth() {
|
||||
pkceMethod: "S256",
|
||||
silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
|
||||
checkLoginIframe: true,
|
||||
scope: "openid profile email",
|
||||
scope: "openid profile email groups",
|
||||
});
|
||||
|
||||
auth.authenticated = authenticated;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="kv">
|
||||
<div class="row">
|
||||
<span class="k mono">Moonlight</span>
|
||||
<span class="k mono">Moonlight host</span>
|
||||
<span class="v mono">{{ wolf.moonlightHost }}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -44,8 +44,9 @@
|
||||
<button class="primary" type="button" :disabled="wolf.unlocking" @click="$emit('unlock')">
|
||||
{{ wolf.unlocking ? "Unlocking..." : "Unlock Moonlight" }}
|
||||
</button>
|
||||
<button class="copy mono" type="button" :disabled="wolf.loading" @click="$emit('refresh')">refresh</button>
|
||||
<button class="copy mono" type="button" :disabled="wolf.loading" @click="$emit('refresh')">Refresh</button>
|
||||
</div>
|
||||
<div class="hint mono">Use the host above in Moonlight after unlocking your current IP.</div>
|
||||
|
||||
<div v-if="wolf.pendingPairRequests.length" class="secret-box">
|
||||
<div class="secret-head">
|
||||
@ -113,3 +114,5 @@ defineProps({
|
||||
|
||||
defineEmits(["refresh", "unlock", "pair", "game-mode", "admin-unlock"]);
|
||||
</script>
|
||||
|
||||
<style scoped src="../styles/account.css"></style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user