Spaces:
Sleeping
Sleeping
File size: 2,248 Bytes
3ba5b62 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | """
Simple username + password authentication for DiffLens.
User credentials are stored in users.json next to this file:
{"users": {"alice": "$2b$12$..."}}
The hash is bcrypt. Sessions use Flask's signed cookie (SECRET_KEY must be
set in the environment for production).
"""
import json
import os
import secrets
from functools import wraps
import bcrypt
from flask import session, jsonify, request
USERS_FILE = os.path.join(os.path.dirname(__file__), "users.json")
def _load_users():
if not os.path.exists(USERS_FILE):
return {}
with open(USERS_FILE, "r") as f:
data = json.load(f) or {}
return data.get("users", {})
def _save_users(users):
with open(USERS_FILE, "w") as f:
json.dump({"users": users}, f, indent=2)
def hash_password(plain):
return bcrypt.hashpw(plain.encode("utf-8"), bcrypt.gensalt(12)).decode("ascii")
def verify_password(plain, hashed):
try:
return bcrypt.checkpw(plain.encode("utf-8"), hashed.encode("utf-8"))
except Exception:
return False
def create_user(username, password):
users = _load_users()
users[username] = hash_password(password)
_save_users(users)
def authenticate(username, password):
users = _load_users()
h = users.get(username)
if not h:
return False
return verify_password(password, h)
def get_secret_key():
"""Returns the Flask SECRET_KEY. Reads from env, falls back to a file
that's auto-generated on first run so the key persists across restarts."""
key = os.environ.get("DIFFLENS_SECRET_KEY")
if key:
return key
path = os.path.join(os.path.dirname(__file__), ".secret_key")
if os.path.exists(path):
with open(path, "r") as f:
return f.read().strip()
key = secrets.token_urlsafe(48)
with open(path, "w") as f:
f.write(key)
os.chmod(path, 0o600)
return key
def login_required(view):
"""Decorator that returns 401 for unauthenticated requests."""
@wraps(view)
def wrapper(*args, **kwargs):
if not session.get("user"):
return jsonify({"error": "unauthorized"}), 401
return view(*args, **kwargs)
return wrapper
def current_user():
return session.get("user")
|