Features: feed CRUD, per-feed ntfy target (incl. private servers), Telegram/webhook channels, keyword filters, image attachments, per-feed intervals, OPML import/export, notification history & stats, users with roles, admin alerts, RU/EN i18n, light/dark theme, notification preview, history search, activity chart. Dockerized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
"""Password hashing and session helpers.
|
||||
|
||||
Uses stdlib PBKDF2 so no native build dependencies are required.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import secrets
|
||||
|
||||
_ALGO = "sha256"
|
||||
_ITERATIONS = 240_000
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
salt = secrets.token_hex(16)
|
||||
digest = hashlib.pbkdf2_hmac(
|
||||
_ALGO, password.encode(), bytes.fromhex(salt), _ITERATIONS
|
||||
).hex()
|
||||
return f"pbkdf2_{_ALGO}${_ITERATIONS}${salt}${digest}"
|
||||
|
||||
|
||||
def verify_password(password: str, stored: str) -> bool:
|
||||
try:
|
||||
scheme, iterations, salt, digest = stored.split("$")
|
||||
if not scheme.startswith("pbkdf2_"):
|
||||
return False
|
||||
algo = scheme.split("_", 1)[1]
|
||||
expected = hashlib.pbkdf2_hmac(
|
||||
algo, password.encode(), bytes.fromhex(salt), int(iterations)
|
||||
).hex()
|
||||
return hmac.compare_digest(expected, digest)
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
Reference in New Issue
Block a user