feat: multi-user auth — per-user spaces, pbkdf2 passwords, session tokens, login page
This commit is contained in:
+31
-1
@@ -1,10 +1,14 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
_TEST_USER = {"username": "testuser", "output_dir": "/tmp", "is_admin": False}
|
||||
|
||||
|
||||
def make_app():
|
||||
from fastapi import FastAPI
|
||||
from api.router import router
|
||||
from api.router import router, current_user
|
||||
app = FastAPI()
|
||||
# Override auth for tests — no real credentials needed
|
||||
app.dependency_overrides[current_user] = lambda: _TEST_USER
|
||||
app.include_router(router)
|
||||
return app
|
||||
|
||||
@@ -14,6 +18,7 @@ def test_status_returns_idle():
|
||||
r = client.get("/status")
|
||||
assert r.status_code == 200
|
||||
assert r.json()["status"] == "idle"
|
||||
assert r.json()["username"] == "testuser"
|
||||
|
||||
|
||||
def test_config_get_returns_dict():
|
||||
@@ -28,3 +33,28 @@ def test_transcripts_returns_list():
|
||||
r = client.get("/transcripts")
|
||||
assert r.status_code == 200
|
||||
assert isinstance(r.json(), list)
|
||||
|
||||
|
||||
def test_status_requires_auth():
|
||||
from fastapi import FastAPI
|
||||
from api.router import router
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app, raise_server_exceptions=False)
|
||||
r = client.get("/status")
|
||||
assert r.status_code == 401
|
||||
|
||||
|
||||
def test_login_rejects_wrong_credentials():
|
||||
import tempfile, os
|
||||
from unittest.mock import patch
|
||||
from fastapi import FastAPI
|
||||
from api.router import router
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app, raise_server_exceptions=False)
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
r = client.post("/login", json={"username": "nobody", "password": "wrong"})
|
||||
assert r.status_code == 401
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import importlib
|
||||
import os
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
def _fresh_auth(tmpdir):
|
||||
"""Reload auth module with a temp users file and clear sessions."""
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
auth._sessions.clear()
|
||||
return os.path.join(tmpdir, "users.toml")
|
||||
|
||||
|
||||
def test_has_users_false_when_empty():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
assert not auth.has_users()
|
||||
|
||||
|
||||
def test_create_and_authenticate():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
auth._sessions.clear()
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
auth.create_user("thomas", "geheim123", "/tmp/transkripte", is_admin=True)
|
||||
token = auth.authenticate("thomas", "geheim123")
|
||||
assert token is not None
|
||||
assert len(token) > 10
|
||||
|
||||
|
||||
def test_authenticate_wrong_password():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
auth._sessions.clear()
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
auth.create_user("thomas", "geheim123", "/tmp/transkripte")
|
||||
assert auth.authenticate("thomas", "falsch") is None
|
||||
|
||||
|
||||
def test_authenticate_unknown_user():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
assert auth.authenticate("niemand", "irgendwas") is None
|
||||
|
||||
|
||||
def test_get_user_for_token():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
auth._sessions.clear()
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
auth.create_user("anna", "secret456", "/tmp/anna")
|
||||
token = auth.authenticate("anna", "secret456")
|
||||
user = auth.get_user_for_token(token)
|
||||
assert user["username"] == "anna"
|
||||
assert user["output_dir"] == "/tmp/anna"
|
||||
|
||||
|
||||
def test_invalidate_token():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
auth._sessions.clear()
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
auth.create_user("bob", "pass789!", "/tmp/bob")
|
||||
token = auth.authenticate("bob", "pass789!")
|
||||
auth.invalidate_token(token)
|
||||
assert auth.get_user_for_token(token) is None
|
||||
|
||||
|
||||
def test_has_users_true_after_create():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
import auth
|
||||
importlib.reload(auth)
|
||||
users_path = os.path.join(tmpdir, "users.toml")
|
||||
with patch("auth.USERS_PATH", users_path):
|
||||
auth.create_user("lisa", "abc123!", "/tmp/lisa")
|
||||
assert auth.has_users()
|
||||
Reference in New Issue
Block a user