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")