mirror of
https://github.com/andrey271192/PCAtelegram_web.git
synced 2026-09-21 12:01:56 +00:00
fix: persist admin sessions across restart
This commit is contained in:
@@ -119,16 +119,37 @@ def public_host_for_notes() -> str:
|
|||||||
return HOST if HOST != "0.0.0.0" else "127.0.0.1"
|
return HOST if HOST != "0.0.0.0" else "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
def session_secret() -> bytes:
|
||||||
|
user, password = load_admin_credentials()
|
||||||
|
return f"{user}:{password}:{ADMIN_AUTH_FILE}".encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def make_session() -> str:
|
def make_session() -> str:
|
||||||
token = secrets.token_urlsafe(32)
|
nonce = secrets.token_urlsafe(24)
|
||||||
SESSIONS[token] = time.time() + SESSION_TTL_SECONDS
|
exp = int(time.time() + SESSION_TTL_SECONDS)
|
||||||
|
payload = f"{exp}.{nonce}"
|
||||||
|
sig = hmac.new(session_secret(), payload.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||||
|
token = f"{payload}.{sig}"
|
||||||
|
SESSIONS[token] = exp
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
def session_is_valid(token: str) -> bool:
|
def session_is_valid(token: str) -> bool:
|
||||||
|
if not token:
|
||||||
|
return False
|
||||||
exp = SESSIONS.get(token)
|
exp = SESSIONS.get(token)
|
||||||
if not exp:
|
if not exp:
|
||||||
return False
|
parts = token.split(".")
|
||||||
|
if len(parts) != 3:
|
||||||
|
return False
|
||||||
|
exp_raw, nonce, sig = parts
|
||||||
|
if not exp_raw.isdigit() or not nonce:
|
||||||
|
return False
|
||||||
|
payload = f"{exp_raw}.{nonce}"
|
||||||
|
expected = hmac.new(session_secret(), payload.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||||
|
if not hmac.compare_digest(sig, expected):
|
||||||
|
return False
|
||||||
|
exp = int(exp_raw)
|
||||||
if exp < time.time():
|
if exp < time.time():
|
||||||
SESSIONS.pop(token, None)
|
SESSIONS.pop(token, None)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -636,6 +636,10 @@ async function api(path, options = {}) {
|
|||||||
if (options.body && !headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
if (options.body && !headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
||||||
const res = await fetch(path, { ...options, headers, credentials: "same-origin" });
|
const res = await fetch(path, { ...options, headers, credentials: "same-origin" });
|
||||||
const data = await res.json().catch(() => ({}));
|
const data = await res.json().catch(() => ({}));
|
||||||
|
if (res.status === 401) {
|
||||||
|
window.location.assign("/");
|
||||||
|
throw new Error("unauthorized");
|
||||||
|
}
|
||||||
if (!res.ok || data.ok === false) throw new Error(data.error || `HTTP ${res.status}`);
|
if (!res.ok || data.ok === false) throw new Error(data.error || `HTTP ${res.status}`);
|
||||||
return data.data ?? data;
|
return data.data ?? data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
document.documentElement.dataset.theme = theme;
|
document.documentElement.dataset.theme = theme;
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
<link rel="stylesheet" href="/styles.css?v=2.5.0-admin25">
|
<link rel="stylesheet" href="/styles.css?v=2.5.0-admin26">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-shell">
|
<div class="app-shell">
|
||||||
@@ -482,6 +482,6 @@
|
|||||||
<button id="qrCopyBtn" type="button" class="soft" data-i18n="copyLink">Copy link</button>
|
<button id="qrCopyBtn" type="button" class="soft" data-i18n="copyLink">Copy link</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/app.js?v=2.5.0-admin25" type="module"></script>
|
<script src="/app.js?v=2.5.0-admin26" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user