Files
csbot-prototype/documentation/07-firmy-a-prava.md
T
JiriUhlirandClaude Opus 5 a771834e57 Realne sluzby, OpenAI, odesilani e-mailu a helpdesk
Katalog srovnany s tim, co opravdu bezi na services.csbot.cz/apps:
trinact sluzeb dostalo pristupove udaje a levne cteci overeni, opravena
appId, ktera nikam nevedla (ppl, microsoft365, transcription), a GA4,
Search Console, Google Ads i Sklik ted stoji na aplikaci analytics,
kazda s vlastnimi udaji. Nove sluzby SAP Business One, Google Workspace
a Meta Ads. K tomu 23 skriptu, ktere s nimi opravdu neco delaji.

OpenAI jako prvni sluzba, ktera nebezi u nas: Service.baseUrl s absolutni
adresou, prepis pres <SLUZBA>_BASE_URL nebo adresu u konektoru, predpona
hlavicky u pole udaju (uzivatel vlepi holy klic, Bearer dopise runtime).
Dotaz na model, nahrani souboru, otazka nad souborem, prepis zvuku.
Skript umi odeslat soubor pres ctx.http.postForm (multipart, obsah Base64).

Sluzba E-mail pres SMTP. Neni to skript, ale vnitrni krok - SMTP neni HTTP.
Konektor nese schranku firmy, krok ma HTML telo, ve kterem se dosazene
hodnoty escapuji (znacky autora sablony jsou zamer, ostre zavorky od
zakaznika ne). Overeni konektoru se prihlasi na server a nic neodesle.

Helpdesk: Ticket.helpdeskSourceId drzi firmu, ktera pozadavek poslala,
vlastnikem zustava ta, ktera ho resi - jinak by ho resitel nemel ve sve
fronte. Komu pozadavek pripadne, urcuje Tenant.helpdeskProviderId.
Zadavatel vidi jen svoje pozadavky a smi k nim pripsat komentar.

Opravy v portalu:
- hlasky o ulozisti a odchozi IP vidi jen spravce platformy
- typ ticketu se v automatizaci vybira ze seznamu firmy, nebo dosadi z dat
- stav ticketu je otevreny naseptavac, ne ciselnik
- ticket jde zalozit rucne, zakaznik u nej neni povinny
- kanal se prejmenoval a parametry u webhooku jsou oznacene jako nepovinne
- srovnane markdown tabulky v cele dokumentaci

Co z teto davky jeste neni: prepinac firmy je porad jen stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 07:40:16 +02:00

5.0 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.customer je 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.

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.

E-mail 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

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