from pathlib import Path import sqlite3 import time import hashlib class AccountStore: def __init__(self, db_path: Path): self.db_path = db_path self._init_db() def _init_db(self) -> None: conn = sqlite3.connect(self.db_path) cur = conn.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS accounts ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password_hash TEXT, created_at INTEGER )""") cur.execute(""" CREATE TABLE IF NOT EXISTS devices ( account_id INTEGER, device_id TEXT, PRIMARY KEY (account_id, device_id) )""") conn.commit() conn.close() def _hash_password(self, password: str) -> str: salt = b"lane-account-salt" return hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, 200_000).hex() def create_account(self, username: str, password: str) -> int: conn = sqlite3.connect(self.db_path) cur = conn.cursor() cur.execute(""" INSERT INTO accounts (username, password_hash, created_at) VALUES (?, ?, ?)""", (username, self._hash_password(password), int(time.time()))) conn.commit() acc_id = cur.lastrowid conn.close() return acc_id def bind_device(self, account_id: int, device_id: str) -> None: conn = sqlite3.connect(self.db_path) cur = conn.cursor() cur.execute(""" INSERT OR IGNORE INTO devices (account_id, device_id) VALUES (?, ?)""", (account_id, device_id)) conn.commit() conn.close() def authenticate(self, username: str, password: str) -> dict | None: conn = sqlite3.connect(self.db_path) cur = conn.cursor() cur.execute("SELECT id, password_hash FROM accounts WHERE username = ?", (username,)) row = cur.fetchone() conn.close() if row is None: return None acc_id, stored_hash = row if stored_hash != self._hash_password(password): return None return {"id": acc_id, "username": username}