feat: account sessions and usable template editors

This commit is contained in:
Pigway
2026-10-02 08:43:07 +08:00
parent 186b56c495
commit 141849d682
12 changed files with 515 additions and 33 deletions
+117
View File
@@ -0,0 +1,117 @@
import hashlib
import time
from pigway_frame_flow.core.accounts import password_hash
def account(app):
app.state.service.db.execute(
"INSERT INTO users VALUES(?,?,1)",
("test-user", password_hash("fixture-password")),
)
def login(client, app, remember=True):
account(app)
client.headers.pop("Authorization", None)
return client.post(
"/api/v1/auth/login",
json={
"username": "test-user",
"password": "fixture-password",
"remember": remember,
},
)
def test_session_login_and_csrf(client, app):
response = login(client, app)
assert response.status_code == 200
assert "HttpOnly" in response.headers["set-cookie"]
assert "SameSite=strict" in response.headers["set-cookie"]
assert client.get("/api/v1/devices").status_code == 200
assert client.post("/api/v1/jobs", json={}).status_code == 403
assert client.get("/api/v1/auth/me").json()["username"] == "test-user"
client.headers["X-CSRF-Token"] = response.json()["csrf_token"]
assert client.post("/api/v1/auth/logout").status_code == 204
assert client.get("/api/v1/devices").status_code == 401
def test_password_change_revokes_sessions(client, app):
response = login(client, app)
client.headers["X-CSRF-Token"] = response.json()["csrf_token"]
assert (
client.put(
"/api/v1/auth/password",
json={"current_password": "wrong", "new_password": "replacement-password"},
).status_code
== 401
)
assert (
client.put(
"/api/v1/auth/password",
json={
"current_password": "fixture-password",
"new_password": "replacement-password",
},
).status_code
== 204
)
assert client.get("/api/v1/auth/me").status_code == 401
assert (
client.post(
"/api/v1/auth/login",
json={"username": "test-user", "password": "replacement-password"},
).status_code
== 200
)
def test_login_rate_limit(client, app):
account(app)
for _ in range(5):
assert (
client.post(
"/api/v1/auth/login",
json={"username": "test-user", "password": "wrong"},
).status_code
== 401
)
assert (
client.post(
"/api/v1/auth/login",
json={"username": "test-user", "password": "fixture-password"},
).status_code
== 429
)
def test_session_expiry_and_no_plaintext(client, app):
response = login(client, app, False)
assert "Max-Age" not in response.headers["set-cookie"]
raw = client.cookies.get("frameflow_session")
row = app.state.service.db.one("SELECT * FROM sessions")
assert row["token_hash"] == hashlib.sha256(raw.encode()).hexdigest()
app.state.service.db.execute("UPDATE sessions SET expires_at=?", (time.time() - 1,))
assert client.get("/api/v1/auth/me").status_code == 401
assert "fixture-password" not in str(
app.state.service.db.one("SELECT * FROM users")
)
def test_reset_template(client):
assert (
client.put(
"/api/v1/templates/quote/config", json={"data": {"title": "Saved title"}}
).status_code
== 200
)
assert (
client.get("/api/v1/templates/quote").json()["defaults"]["title"]
== "Saved title"
)
assert client.delete("/api/v1/templates/quote/config").status_code == 204
assert (
client.get("/api/v1/templates/quote").json()["defaults"]["title"]
!= "Saved title"
)