57 lines
1.8 KiB
Python
57 lines
1.8 KiB
Python
from fastapi.testclient import TestClient
|
|
|
|
|
|
def test_failed_login_generic_and_no_cookie():
|
|
pass # covered in test_auth; kept for grouping
|
|
|
|
|
|
def test_csrf_required_on_bike_create(client):
|
|
client.login()
|
|
# No csrf token at all -> must be rejected, bike NOT created.
|
|
resp = client.post("/bikes/new", data={"name": "Sneaky"})
|
|
assert "CSRF check failed" in resp.text
|
|
|
|
|
|
def test_wrong_csrf_rejected(client):
|
|
client.login()
|
|
resp = client.post("/bikes/new", data={"csrf": "not-the-right-token", "name": "Sneaky"})
|
|
assert "CSRF check failed" in resp.text
|
|
|
|
|
|
def test_login_cookie_attributes(client):
|
|
r = client.login()
|
|
sc = r.headers.get("set-cookie", "")
|
|
low = sc.lower()
|
|
assert "httponly" in low
|
|
assert "samesite=lax" in low
|
|
# Dev (not production, http) => no Secure flag.
|
|
assert "secure" not in low
|
|
|
|
|
|
def test_production_sets_secure_cookie(monkeypatch):
|
|
monkeypatch.setenv("BIKE_ADMIN_IS_PRODUCTION", "true")
|
|
from app.config import get_settings
|
|
|
|
get_settings.cache_clear()
|
|
from app.main import app
|
|
|
|
with TestClient(app, follow_redirects=False) as c: # lifespan seeds the user
|
|
r = c.post("/login", data={"username": "admin", "password": "correct-horse-battery-staple"})
|
|
sc = r.headers.get("set-cookie", "").lower()
|
|
assert r.status_code == 303
|
|
assert "secure" in sc
|
|
|
|
|
|
def test_login_throttle(client, monkeypatch):
|
|
monkeypatch.setenv("BIKE_ADMIN_LOGIN_MAX_ATTEMPTS", "3")
|
|
from app.config import get_settings
|
|
|
|
get_settings.cache_clear()
|
|
for _ in range(3):
|
|
client.post("/login", data={"username": "admin", "password": "wrong"})
|
|
# 4th attempt: even the correct password is blocked.
|
|
pw = "correct-horse-battery-staple"
|
|
r = client.post("/login", data={"username": "admin", "password": pw})
|
|
assert r.status_code == 200
|
|
assert "too many" in r.text.lower()
|