1d0ff2f72e
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m20s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m29s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 5s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 5s
build / build (push) Successful in 1m39s
Testy / Testy warstwy logicznej (silnik) (push) Failing after 4m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m32s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m25s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 6s
Wymaganie było mocniejsze niż schowanie przycisku: po niedostępnej funkcji nie może zostać śladu w źródle strony. Największy wyciek nie był przyciskiem — _prompt_block wstrzykiwał w stronę CAŁY katalog modeli jako JSON (dostawcy, nazwy modeli, rozmiary okien kontekstu), na każdym ekranie z generowaniem, niezależnie od uprawnień konta. Druga dziura była głębsza: handlery nie sprawdzały nic. Trasa /interpret musi być dostępna dla konta z Interpretacjami, więc granica przebiega WEWNĄTRZ niej, po polu `action` — spreparowany formularz z action=prompt generował tekst, a action=export pobierał arkusz, mimo że szablon chował oba przyciski. Akcja bez uprawnienia wraca do akcji domyślnej ekranu zamiast dawać błąd: komunikat „brak uprawnień do generowania" sam w sobie mówiłby, że taka funkcja istnieje. Ślady wycięte także tam, gdzie nie były kontrolką: znaczniki natalNote/ reportNatal, pliki models.js/progress.js/natal.js/predictions.js oraz podtytuł ekranu Skompiluj, który wymieniał interpretację od AI z nazwy. 12 testów; 9 z nich pada na kodzie sprzed poprawki (sprawdzone przez cofnięcie zmian w app/). Kontrola pozytywna pilnuje, żeby nie przechodziły dlatego, że generowanie jest zepsute dla wszystkich. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
5.5 KiB
Markdown
112 lines
5.5 KiB
Markdown
# Konta i uprawnienia (PRE-27)
|
|
|
|
Rozszerzenie kont imiennych z [PRE-17](konta-i-audyt.md): konta zakłada się
|
|
**z aplikacji**, a każde dostaje własny zestaw funkcji.
|
|
|
|
## Dwie zasady, z których wynika reszta
|
|
|
|
**1. Konto ograniczone widzi program KOMPLETNY — tylko mniejszy.**
|
|
Nic nie może zdradzać, że istnieje coś więcej. Żadnych wyszarzonych zakładek,
|
|
żadnego „brak uprawnień", żadnego 403 — bo **403 samo w sobie jest informacją**,
|
|
że pod tym adresem coś jest. Ścieżka bez uprawnienia odpowiada **404**, tak samo
|
|
jak adres, którego nie ma.
|
|
|
|
Z tej zasady wynikło też wyłączenie `/docs`, `/redoc` i `/openapi.json`.
|
|
Automatyczna dokumentacja FastAPI wypisuje komplet tras — czyli spis wszystkich
|
|
funkcji programu. Ochrona zakładek nic by nie dała, gdyby obok leżał ich katalog.
|
|
(Znalezione testem, nie przeglądem kodu.)
|
|
|
|
**2. Konto administracyjne pochodzi WYŁĄCZNIE ze środowiska.**
|
|
`APP_USER` / `APP_PASSWORD` (albo `APP_USERS`) — jak dotąd. To konto ma wszystkie
|
|
uprawnienia i jako jedyne zarządza pozostałymi. **Nie leży w pliku kont**, więc
|
|
nie da się go skasować ani ograniczyć z ekranu — nawet przez pomyłkę, nawet
|
|
spreparowanym żądaniem. Konto założone w pliku o tym samym loginie **nie
|
|
przesłoni** administracyjnego (kolejność sprawdzania jest odwrotna).
|
|
|
|
## Podział funkcji
|
|
|
|
**Ekrany** — zakładki widoczne w nawigacji:
|
|
|
|
| klucz | zakładka |
|
|
|---|---|
|
|
| `chart` | Horoskop |
|
|
| `interpret` | Interpretacje |
|
|
| `timeline` | Kalendarz |
|
|
| `synastry` | Synastria |
|
|
| `significators` | Sygnifikatory |
|
|
| `compile` | Skompiluj |
|
|
| `settings` | Ustawienia |
|
|
|
|
**Rozszerzenia** — poziomy złożoności wewnątrz ekranów:
|
|
|
|
| klucz | co daje |
|
|
|---|---|
|
|
| `houses_compare` | wybór systemu domów, zestawienie kilku obok siebie, obrót koła |
|
|
| `extra_charts` | aspektarian, wykres deklinacji, oś antyscji |
|
|
| `advanced_calc` | stacje planet, tabele żywiołów i faz, aspekty poboczne, zodiaki syderyczne |
|
|
| `ai` | horoskopy pisane przez model językowy (**każde użycie kosztuje**) |
|
|
| `export` | pobieranie PDF i Excela |
|
|
|
|
Konto bez `houses_compare` dostaje horoskop w Whole Sign i **nie widzi**, że
|
|
systemów jest trzynaście. Konto bez `ai` nie zobaczy przycisku generowania ani
|
|
nie wywoła go z pominięciem interfejsu.
|
|
|
|
## Gdzie leży granica
|
|
|
|
W handlerze, nie w szablonie. Ukrycie pola w formularzu chroni przed przypadkiem,
|
|
ale nie przed kimś, kto zna nazwy pól — dlatego `_limit_options()` ścina opcje
|
|
**po stronie serwera**, a rysunki dodatkowe bez uprawnienia w ogóle nie powstają
|
|
(nie ma ich nawet w źródle strony).
|
|
|
|
Mapa `trasa → uprawnienie` jest **jedna**, w `app/features.py`. Rozproszenie jej
|
|
po dekoratorach kończy się trasą, o której ochronie ktoś zapomniał — a taka dziura
|
|
jest niewidoczna do chwili, gdy ktoś ją znajdzie. Trasa bez wpisu w mapie wymaga
|
|
uprawnień administracyjnych: **przeoczenie ma zamykać, nie otwierać**. Test
|
|
przechodzi po wszystkich trasach aplikacji i wymaga, by każda była opisana.
|
|
|
|
### Gdy jedna trasa robi kilka rzeczy
|
|
|
|
Mapa tras nie wystarcza tam, gdzie jedna trasa obsługuje kilka funkcji naraz.
|
|
`POST /interpret` liczy horoskop, ale to samo pole `action` prosi o wygenerowanie
|
|
promptu, napisanie horoskopu przez model albo eksport arkusza. Konto, które ma
|
|
mieć Interpretacje bez generowania, musi dostać tę trasę — więc granica przebiega
|
|
wewnątrz niej, po akcjach: `_AKCJE_POD_UPRAWNIENIEM` przypisuje akcji uprawnienie,
|
|
a `_dozwolona_akcja()` sprowadza żądanie bez uprawnienia do akcji domyślnej ekranu.
|
|
|
|
Sprowadza — nie odrzuca. Komunikat „brak uprawnień do generowania" sam w sobie
|
|
mówiłby, że taka funkcja istnieje, czyli łamałby zasadę drugą po to, żeby
|
|
wyegzekwować pierwszą. Akcja bez uprawnienia ma wyglądać na literówkę w formularzu.
|
|
|
|
### Ślad to nie tylko przycisk
|
|
|
|
Wymaganie brzmi „nie może być śladu", i to jest mocniejsze niż schowanie kontrolki.
|
|
Największym wyciekiem po stronie generowania nie był przycisk, tylko **katalog
|
|
modeli** — nazwy dostawców, nazwy modeli i rozmiary okien kontekstu — wstrzykiwany
|
|
w stronę blokiem JSON na każdym ekranie z generowaniem, niezależnie od uprawnień.
|
|
Dlatego `_llm_catalog_for()` oddaje pusty katalog kontu bez uprawnienia, a szablony
|
|
trzymają pod bramką także znaczniki (`natalNote`, `reportNatal`), pliki skryptów
|
|
(`models.js`, `progress.js`, `natal.js`, `predictions.js`) i **zdania opisujące
|
|
funkcję** — podtytuł ekranu Skompiluj wymieniał interpretację od AI z nazwy.
|
|
|
|
Testu na to nie da się napisać przez „sprawdź, czy przycisku nie ma": trzeba
|
|
sprawdzić, że w źródle strony nie ma żadnego z tych śladów, i mieć kontrolę
|
|
pozytywną, że przy uprawnieniu wszystkie są. Inaczej test przechodzi także wtedy,
|
|
gdy generowanie jest zepsute dla wszystkich.
|
|
|
|
## Gdzie leżą konta
|
|
|
|
Plik JSON wskazany przez `ACCOUNTS_FILE` (domyślnie `/app/state/accounts.json`),
|
|
na NFS — **własny podkatalog prezentacji**, nie katalog z bazami: zamontowanie
|
|
tutaj całego udziału obeszłoby bokiem zamknięcie dostępu z DAN-25.
|
|
|
|
Hasła wyłącznie jako hash scrypt, tym samym mechanizmem co `APP_USERS` — jedna
|
|
implementacja, więc nie ma czego rozjechać. Zapis jest **atomowy** (plik
|
|
tymczasowy + `os.replace` w tym samym katalogu): przerwanie zapisu nie obetnie
|
|
pliku, czyli nie skasuje wszystkich kont naraz.
|
|
|
|
## Czego ten mechanizm NIE robi
|
|
|
|
Nie zastępuje ochrony baz na poziomie sieci ani NFS (DAN-25). Ktoś z dostępem do
|
|
udziału albo do warstwy danych nadal je odczyta — uprawnienia w aplikacji
|
|
ograniczają to, co widać **przez aplikację**, i tyle.
|