Spaces:
Sleeping
Sleeping
| """ | |
| 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.""" | |
| 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") | |