43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
"""Collector (machine) authentication.
|
|
|
|
Dashboard *users* are authenticated by nginx in front of the app (basic auth today,
|
|
oauth2-proxy + Microsoft Entra ID later), so nothing here deals with people.
|
|
"""
|
|
|
|
import hashlib
|
|
import secrets
|
|
import sqlite3
|
|
|
|
from fastapi import Depends, Header, HTTPException
|
|
|
|
from .db import get_db
|
|
from .timeutil import to_db, utcnow
|
|
|
|
KEY_PREFIX = "bdk_"
|
|
|
|
|
|
def generate_key() -> str:
|
|
return KEY_PREFIX + secrets.token_urlsafe(32)
|
|
|
|
|
|
def hash_key(key: str) -> str:
|
|
# Keys are 256-bit random, so a fast unsalted hash is fine (no dictionary to attack).
|
|
return hashlib.sha256(key.encode()).hexdigest()
|
|
|
|
|
|
def require_ingest_key(
|
|
authorization: str | None = Header(None),
|
|
conn: sqlite3.Connection = Depends(get_db),
|
|
) -> str:
|
|
scheme, _, token = (authorization or "").partition(" ")
|
|
token = token.strip()
|
|
if scheme.lower() != "bearer" or not token:
|
|
raise HTTPException(401, "Missing bearer token", headers={"WWW-Authenticate": "Bearer"})
|
|
row = conn.execute(
|
|
"SELECT id, name FROM api_keys WHERE key_hash = ? AND revoked_at IS NULL", (hash_key(token),)
|
|
).fetchone()
|
|
if row is None:
|
|
raise HTTPException(401, "Invalid or revoked API key", headers={"WWW-Authenticate": "Bearer"})
|
|
conn.execute("UPDATE api_keys SET last_used_at = ? WHERE id = ?", (to_db(utcnow()), row["id"]))
|
|
return row["name"]
|