Freunde, privates Teilen, Artikelrechte, Reiserouten
- Freunde (freunde.py, Tabelle friendships): suchen, anfragen, annehmen, ablehnen, entfernen; neue Seite "Freunde" im Menü - Vorratslisten und Kanban-Projekte immer privat: sichtbar für Ersteller und Mitglieder; teilen nur mit Freunden (Admins: mit allen); Kanban-Mitglieder (Tabelle kanban_project_members), Bearbeiter nur aus dem Projekt, ausgetretene Mitglieder verlieren ihre Zuteilung - Artikel: lesen alle; bearbeiten Autor, Mitarbeitende verknüpfter Kanban-Projekte und Admins; verknüpfen nur bearbeitbare Artikel (sonst ließen sich Rechte erschleichen); Board-Liste auf der Artikelseite zeigt nur sichtbare Boards - Administratoren sehen und bearbeiten alles - Reisen: Städte nur von Freunden (Admins: alle); "Reiserouten zeigen" zeichnet die Strecken der Kilometer-Rechnung als Bögen - Migration Schritt 6: bisher gemeinsame Listen und alle Projekte bekommen die vorhandenen Konten als Mitglieder, ohne Ersteller → erster Admin; erste Liste einer neuen Installation gehört dem ersten Konto - Tests: test_freunde.py, test_zugriff.py, bestehende auf Privat-Modell umgestellt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d7497bead
commit
0fb3521c2f
34 files changed
+833
-245
No files matched your search
@@ -0,0 +1,95 @@
|
||||
"""Privat-Modell: Kanban-Projekte nur für Mitglieder, Artikel lesen alle / bearbeiten Beteiligte, Admins dürfen alles."""
|
||||
from app import db
|
||||
|
||||
|
||||
def projekt(client, name="P", members=(), links=()):
|
||||
res = client.post("/api/kanban/projekte", json={"name": name, "members": list(members), "links": list(links)})
|
||||
assert res.status_code == 201, res.text
|
||||
return res.json()["id"]
|
||||
|
||||
|
||||
def artikel(client, title="Doku"):
|
||||
res = client.post("/api/projects", json={"title": title})
|
||||
assert res.status_code == 201, res.text
|
||||
return res.json()["id"]
|
||||
|
||||
|
||||
def test_kanban_privat(login, befriend):
|
||||
anna, ben, chef = login("anna"), login("ben"), login("chef", admin=True)
|
||||
pid = projekt(anna)
|
||||
assert ben.get("/api/kanban").json()["projects"] == []
|
||||
assert ben.get(f"/api/kanban/projekte/{pid}").status_code == 404
|
||||
assert ben.post(f"/api/kanban/projekte/{pid}/aufgaben", json={"title": "x"}).status_code == 404
|
||||
assert chef.get(f"/api/kanban/projekte/{pid}").status_code == 200 # Admin sieht alles
|
||||
# Mitglied nur als Freund
|
||||
assert anna.patch(f"/api/kanban/projekte/{pid}", json={"members": [ben.user["id"]]}).status_code == 400
|
||||
befriend(anna, ben)
|
||||
data = anna.patch(f"/api/kanban/projekte/{pid}", json={"members": [ben.user["id"]]}).json()
|
||||
assert data["project"]["members"] == [ben.user["id"]]
|
||||
assert ben.get(f"/api/kanban/projekte/{pid}").status_code == 200
|
||||
# Mitglieder verwalten nur Ersteller/Admin
|
||||
assert ben.patch(f"/api/kanban/projekte/{pid}", json={"members": []}).status_code == 403
|
||||
|
||||
|
||||
def test_bearbeiter_nur_beteiligte(login, befriend):
|
||||
anna, ben, carl = login("anna"), login("ben"), login("carl")
|
||||
befriend(anna, ben)
|
||||
pid = projekt(anna, members=[ben.user["id"]])
|
||||
data = anna.post(f"/api/kanban/projekte/{pid}/aufgaben", json={"title": "A", "assignee": ben.user["id"]}).json()
|
||||
task = data["tasks"][0]
|
||||
assert [p["name"] for p in data["people"]] == ["Anna", "Ben"]
|
||||
assert anna.patch(f"/api/kanban/aufgaben/{task['id']}", json={"assignee": carl.user["id"]}).status_code == 400
|
||||
# Ben fliegt raus → seine Aufgaben sind niemandem mehr zugeteilt
|
||||
data = anna.patch(f"/api/kanban/projekte/{pid}", json={"members": []}).json()
|
||||
assert data["tasks"][0]["assignee"] is None
|
||||
|
||||
|
||||
def test_artikel_lesen_alle_bearbeiten_beteiligte(login, befriend):
|
||||
anna, ben, carl, chef = login("anna"), login("ben"), login("carl"), login("chef", admin=True)
|
||||
aid = artikel(anna)
|
||||
assert ben.get(f"/api/projects/{aid}").json()["canEdit"] is False
|
||||
assert ben.patch(f"/api/projects/{aid}", json={"title": "x"}).status_code == 403
|
||||
# Ben arbeitet an einem verknüpften Kanban-Projekt mit → darf bearbeiten
|
||||
befriend(anna, ben)
|
||||
projekt(anna, members=[ben.user["id"]], links=[aid])
|
||||
assert ben.get(f"/api/projects/{aid}").json()["canEdit"] is True
|
||||
assert ben.patch(f"/api/projects/{aid}", json={"title": "Neu"}).status_code == 200
|
||||
assert carl.get(f"/api/projects/{aid}").status_code == 200 # lesen dürfen alle
|
||||
assert carl.patch(f"/api/projects/{aid}", json={"title": "x"}).status_code == 403
|
||||
assert chef.patch(f"/api/projects/{aid}", json={"title": "Admin"}).status_code == 200
|
||||
# Löschen bleibt Autor/Admin
|
||||
assert ben.delete(f"/api/projects/{aid}").status_code == 403
|
||||
|
||||
|
||||
def test_verknuepfen_nur_bearbeitbare_artikel(login):
|
||||
"""Sonst könnte man sich durch ein eigenes Kanban-Projekt Bearbeitungsrechte verschaffen."""
|
||||
anna, carl = login("anna"), login("carl")
|
||||
aid = artikel(anna)
|
||||
res = carl.post("/api/kanban/projekte", json={"name": "Trick", "links": [aid]})
|
||||
assert res.status_code == 400
|
||||
assert carl.patch(f"/api/projects/{aid}", json={"title": "x"}).status_code == 403
|
||||
|
||||
|
||||
def test_board_auf_artikelseite_nur_sichtbare(login):
|
||||
anna, ben = login("anna"), login("ben")
|
||||
aid = artikel(anna)
|
||||
projekt(anna, name="Intern", links=[aid])
|
||||
assert [b["name"] for b in anna.get(f"/api/projects/{aid}").json()["boards"]] == ["Intern"]
|
||||
assert ben.get(f"/api/projects/{aid}").json()["boards"] == []
|
||||
|
||||
|
||||
def test_umstellung_privat(conn, login):
|
||||
"""Schritt 6: bisher gemeinsame Listen/Projekte bekommen alle Konten als Mitglieder, ohne Besitzer → erster Admin."""
|
||||
anna, ben, chef = login("anna"), login("ben"), login("chef", admin=True)
|
||||
with conn:
|
||||
conn.execute("INSERT INTO shopping_lists (id, name, everyone, position, created_at, created_by) VALUES (50, 'Alt', 1, 1, 'x', ?)", (anna.user["id"],))
|
||||
conn.execute("INSERT INTO shopping_lists (id, name, everyone, position, created_at) VALUES (51, 'Ohne', 1, 2, 'x')")
|
||||
conn.execute("INSERT INTO kanban_projects (id, name, position, created_at, created_by, updated_at) VALUES (60, 'K', 1, 'x', ?, 'x')", (anna.user["id"],))
|
||||
conn.execute("PRAGMA user_version = 5")
|
||||
db.init()
|
||||
members = lambda lid: sorted(r[0] for r in conn.execute("SELECT user_id FROM shopping_list_members WHERE list_id = ?", (lid,)))
|
||||
assert members(50) == sorted([ben.user["id"], chef.user["id"]])
|
||||
assert conn.execute("SELECT created_by FROM shopping_lists WHERE id = 51").fetchone()[0] == chef.user["id"]
|
||||
assert sorted(r[0] for r in conn.execute("SELECT user_id FROM kanban_project_members WHERE project_id = 60")) == sorted([ben.user["id"], chef.user["id"]])
|
||||
assert conn.execute("SELECT COUNT(*) FROM shopping_lists WHERE everyone = 1").fetchone()[0] == 0
|
||||
assert any(l["id"] == 50 for l in ben.get("/api/haushalt").json()["lists"])
|
||||
Reference in new issue
Block a user