Prepinac byl stav uvnitr stranky Prehled, takze prepnuti na LogiTrans zmenilo prehled a nic jineho - ostatni stranky volaly API bez tenantId a server sahnul po vychozi firme. V Lidech tak zustali lide Automie. - vybrana firma je jedna hodnota pro cely dashboard (web/src/lib/tenant.ts) a prezije obnoveni stranky - tenantId doplnuje apiFetch, ne jednotlive stranky. Kdyz si to mela pridavat kazda stranka sama, vetsina na to zapomnela - a prave to byla ta chyba. Nedoplnuje se, kdyz cesta uz firmu nese, u scope=all a mimo /api/dashboard. - useApiQuery se pri prepnuti zepta znovu, jinak by na strance zustala cisla predchozi firmy - prepinac je v hlavicce nad obsahem a nahradil text, ktery ukazoval natvrdo prvni firmu ze seznamu bez ohledu na vyber - ulozena firma, do ktere uzivatel uz nepatri, spadne na vychozi Zjisteno u toho a zapsane do 07-firmy-a-prava.md: prava se scitaji pres vsechny firmy, neprepocitavaji se podle vybrane. Data se tim neprolomi, server je filtruje dal, ale tlacitka ukazuji vic, nez by mela. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.7 KiB
07 - Firmy a prava
Dve ruzne "firmy"
Snadno se pletou, proto hned na zacatku:
- Tenant je firma, ktera portal pouziva. Ma v nem svuj tym, svoje tickety a svoje automatizace. Je to hranice viditelnosti.
ticket.customerje zakaznik toho tenanta, tedy kdo pozadavek poslal. S pravy nema nic spolecneho.
Tenanti jsou v src/data/tenants.ts.
Uzivatel muze byt ve vic firmach
Proto memberships, ne jedno tenantId. Typicky externista, ktery dela
servicedesk dvema klientum a v kazdem ma jinou roli.
interface User {
id: string;
email: string;
name: string;
platformAdmin: boolean; // vidi napric vsemi firmami
memberships: Membership[]; // { tenantId, role }
}
type TenantRole = 'admin' | 'agent';
Role je vzdy az uvnitr firmy. Pristup napric firmami je zvlast jako
platformAdmin - to je nase pravo, ne zakaznicke. Kdyby byla role jen jedna
globalni, nesla by tahle situace vubec zapsat.
Tri pohledy na tickety
| Pohled | Co ukazuje | Kdo smi |
|---|---|---|
all |
napric vsemi firmami | jen platformAdmin |
tenant |
cela jedna firma | kdokoliv, kdo do ni patri |
mine |
jen tickety prihlaseneho | kdo ma navazaneho resitele |
Posilaji se jako query: ?scope=tenant&tenantId=tnt_automia.
U tenant a mine je potreba vedet kterou firmu, protoze uzivatel jich
muze mit vic. Bez tenantId se pouzije prvni.
Prepinac firmy je nad celym portalem
Vybrana firma je jedna hodnota pro cely dashboard (web/src/lib/tenant.ts),
ne stav jedne stranky. Prepinac je v hlavicce nad obsahem a apiFetch doplnuje
tenantId do kazdeho dotazu na /api/dashboard.
Doplnuje se to v API vrstve schvalne, ne na strankach. Kdyz si to mela pridavat kazda stranka sama, vetsina na to zapomnela a server sahnul po vychozi firme - uzivatel se prepnul na LogiTrans a v Lidech koukal na lidi Automie. To neni nepohodli, to jsou cizi data pod hlavickou jine firmy.
Vyjimky, kdy se nedoplnuje:
| Kdy | Proc |
|---|---|
Cesta uz tenantId nese |
proklik z widgetu na konkretni firmu ma prednost |
scope=all |
pohled pres vsechny firmy, jedna firma tam nic neznamena |
| Neni vybrano | server pouzije vychozi firmu uctu |
Cesta mimo /api/dashboard |
prihlaseni a verejny web s firmou nepracuji |
Volba prezije obnoveni stranky (localStorage). Kdyz ulozena firma uz mezi
access.tenants neni - clovek z ni odesel, prihlasil se nekdo jiny na tomtez
pocitaci - spadne se na vychozi firmu uctu, aby kazdy dotaz nekoncil na 403.
Server je autorita
Vsechno rozhoduje src/data/access.ts, jedno misto pro cely portal. Kdyby se to
rozlezlo po routach, driv nebo pozdeji vznikne endpoint, ktery filtr zapomene.
GET /api/dashboard/access vraci, co uzivatel smi:
{
"scopes": ["all", "tenant", "mine"],
"tenants": [{ "id": "tnt_automia", "name": "Automia" }],
"defaultTenantId": "tnt_automia",
"canAssignOthers": true,
"personId": "ppl_uhlir"
}
Klient podle toho kresli prepinac. Nesmi si to dovozovat sam - jinak by se prava pocitala na dvou mistech a jednou se rozejdou.
Nikdy tise nezuzujeme
Pozadavek na pohled nebo firmu, na kterou uzivatel nema pravo, vraci chybu, ne potichu zuzeny vysledek:
| Situace | Odpoved |
|---|---|
| pohled bez opravneni | 403 |
| firma, do ktere nepatri | 404 |
| ucet bez firmy | 403 |
| prirazeni ostatnim bez prava | 403 |
Duvod: kdyby se pozadavek na cizi firmu jen prepnul na vlastni, uzivatel by koukal na cizi cisla v domneni, ze jsou spravna. To je horsi nez chyba.
Filtr v ulozisti je povinny
listTickets, listPeople a listAutomations maji tenantIds jako povinny
argument, ne volitelny. Zapomenuty filtr tak neznamena "vse", ale nezkompiluje se.
Zapis (assignTicket, updateTicketStatus, addComment, updateAutomation,
deleteAutomation) bere tenantIds taky. Cizi zaznam se chova jako neexistujici,
tedy 404, ne 403 - z odpovedi nemá jit poznat, ze takove ID vubec existuje.
Vyjimka je verejny webhook. Ten se autorizuje tokenem v adrese, ne prihlasenim, takze si automatizaci najde pres vsechny firmy.
Jedina vyjimka: helpdesk
Ticket patri jedne firme a to plati dal. U pozadavku z helpdesku ale figuruji
dve: vlastnikem je ta, ktera ho resi, a v helpdeskSourceId je ta, ktera ho
poslala. Zadavatel se k nemu dostane jen pres helpdesk a jen ke svym
pozadavkum; bezny seznam ticketu zustava vlastnikovi.
Neni to obchazeni hranice, je to druha cesta dovnitr s vlastnim scopem. Popis je v 18-ticketovaci-system.md.
Prirazeni jen v ramci firmy
assignTicket odmitne resitele z jine firmy. Jinak by ticket zmizel z prehledu
sve firmy a objevil se nekomu, kdo do ni nepatri.
Prehazovat praci mezi lidmi smi jen admin. agent si smi vzit ticket na sebe,
ale nemuze ho poslat kolegovi - to hlida canAssignOthers.
Demo ucty
Heslo je u vsech demo1234.
| Kdo je | |
|---|---|
admin@automia.cz |
spravce platformy, vidi vsechny tri firmy |
karel.vomacka@automia.cz |
agent v Automii, admin u Nordisu - dve firmy |
martin.kriz@automia.cz |
bezny resitel jedne firmy |
Druhy ucet je ten zajimavy: ukazuje prepinac firem i to, ze prava se lisi podle toho, ktera firma je prave zvolena.
Co chybi
Prava se scitaji pres vsechny firmy, neprepocitavaji se podle vybrane.
permissionsOf(user) vezme role ze vsech clenstvi dohromady. Kdo je spravce
v jedne firme a resitel v druhe, ma prava spravce i po prepnuti do te druhe.
Data se tim neprolomi - server je porad filtruje podle firmy - ale tlacitka
ukazuji vic, nez by mela. Napravit to znamena predat firmu do permissionsOf
a prepocitat cache za dvojici uzivatel a firma.
| Chybi | Poznamka |
|---|---|
| Sprava clenstvi z portalu | memberships jdou zmenit jen v kodu |
| Pozvanky uzivatelu | zadny onboarding |
| Tenant u incidentu | incidenty jsou zatim spolecne, nefiltruji se |
| Tenant u konektoru | katalog je spolecny, napojeni se zatim neeviduje |
| Audit pristupu | odepreni se jen loguje, nikde se neuklada |