feat(astroklient): pule plików per konto i izolacja od produkcji (PRE-29)
Demo ma być rozdawane szeroko i różnym osobom, więc pierwsza wersja — jedno konto na produkcyjnej warstwie danych — nie nadawała się do użycia: każdy dostawałby dostęp do oryginalnych baz, a wgrania jednego klienta widzieliby wszyscy. IZOLACJA OD PRODUKCJI. Warstwa danych i logiczna demo są osobne (manifesty w repo deploy). Osobna musi być TEŻ LOGICZNA, bo zna ona jeden adres warstwy danych — demo korzystające z produkcyjnej logiki i tak trafiłoby na produkcyjne bazy. PULE PER KONTO w warstwie danych. Zapytanie i lista plików niosą nazwę puli; puste = cały udział, czyli produkcja działa dokładnie jak dotąd i o pulach nic nie wie. Nazwa puli przechodzi przez sito dopuszczające wyłącznie znaki bezpieczne w nazwie katalogu — „../..” albo ukośnik wyprowadziłyby zapytanie wprost do cudzych baz, więc sito ZAMIENIA podejrzane znaki zamiast ufać, że nikt ich nie poda. PULA MUSI BYĆ W KLUCZU CACHE ZAPYTAŃ. Bez tego wynik policzony dla jednego konta trafiłby z cache do drugiego — cicha wymiana treści baz między klientami, niewidoczna w logach i nie do wykrycia z zewnątrz. Osobny test tego pilnuje. PULA WYNIKA Z LOGINU, nigdy z żądania. Klient warstwy logicznej jest budowany per żądanie i związany z pulą zalogowanej osoby; gdyby nazwa przychodziła z formularza, wystarczyłoby podstawić cudzy login. Test wysyła `tenant`, `user` i `login` w polach formularza i sprawdza, że nie mają na nią wpływu. Pulę wstrzykujemy w INSTANCJĘ klienta, nie w sygnatury metod. Argumentem trzeba by ją przeprowadzić przez protokół DataSource i build_report — kod, który o kontach nie ma prawa nic wiedzieć — a każde nowe wywołanie byłoby okazją, żeby o nią zapomnieć i sięgnąć nie tam. Konta demo to lista `login:sekret` (DEMO_USERS), bo jedno wspólne konto oznaczałoby wspólną pulę. Format i skrypt haseł te same, co w głównej aplikacji. Pula klienta to JEDEN KATALOG, więc przejście na pełną wersję nie oznacza utraty wgrań — procedurę importu opisuje runbook w repo deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,8 +32,15 @@ def _link() -> link_crypto.Link | None:
|
||||
|
||||
|
||||
class LogicClient:
|
||||
def __init__(self, base_url: str | None = None) -> None:
|
||||
"""Rozmówca warstwy logicznej ZWIĄZANY Z PULĄ KONTA (PRE-29).
|
||||
|
||||
Pulę podaje się przy tworzeniu klienta, a bierze się ją WYŁĄCZNIE z loginu
|
||||
zalogowanej osoby — nigdy z pola formularza. Gdyby przychodziła z żądania,
|
||||
wystarczyłoby podstawić cudzy login, żeby czytać cudze bazy."""
|
||||
|
||||
def __init__(self, tenant: str = "", base_url: str | None = None) -> None:
|
||||
self.base_url = (base_url or settings.logic_url).rstrip("/")
|
||||
self.tenant = tenant or ""
|
||||
|
||||
def report(self, when_utc_iso: str, lat: float, lon: float) -> dict[str, Any]:
|
||||
"""Interpretacja urodzeniowa: sygnifikatory z obliczeń szukane w bazach.
|
||||
@@ -41,7 +48,8 @@ class LogicClient:
|
||||
Jedyne pytanie, jakie astroklient zadaje o treść — bez grupowania,
|
||||
bez promptów, bez modelu językowego."""
|
||||
payload = {"when_utc": when_utc_iso, "lat": lat, "lon": lon,
|
||||
"limit": settings.report_limit, "group": False}
|
||||
"limit": settings.report_limit, "group": False,
|
||||
"tenant": self.tenant}
|
||||
with httpx.Client(timeout=settings.http_timeout) as client:
|
||||
return link_crypto.call_json(client, "POST", f"{self.base_url}/chart/report",
|
||||
payload=payload, headers=_auth_headers(), link=_link())
|
||||
@@ -50,7 +58,8 @@ class LogicClient:
|
||||
with httpx.Client(timeout=max(settings.http_timeout, 60.0)) as client:
|
||||
return link_crypto.call_json(
|
||||
client, "POST", f"{self.base_url}/files/upload",
|
||||
payload={"filename": filename, "content_b64": content_b64, "by": by},
|
||||
payload={"filename": filename, "content_b64": content_b64,
|
||||
"by": by, "tenant": self.tenant},
|
||||
headers=_auth_headers(), link=_link())
|
||||
|
||||
def files_status(self, path: str, status: str, by: str = "") -> dict[str, Any]:
|
||||
@@ -60,7 +69,7 @@ class LogicClient:
|
||||
with httpx.Client(timeout=settings.http_timeout) as client:
|
||||
return link_crypto.call_json(
|
||||
client, "POST", f"{self.base_url}/files/status",
|
||||
payload={"path": path, "status": status, "by": by},
|
||||
payload={"path": path, "status": status, "by": by, "tenant": self.tenant},
|
||||
headers=_auth_headers(), link=_link())
|
||||
|
||||
def health(self) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user