bmc_hub/docs/remote-control.md

62 lines
8.0 KiB
Markdown
Raw Permalink Normal View History

# Fjernbetjening til BMC Hub
Implementeret lokalt. Funktionen er slået fra som standard og er ikke aktiveret i produktion.
## Aktivering og pilot
1. Tag den normale databasebackup. Anvend kun migration `1069_remote_control.sql` gennem migrationscentret eller `scripts/run_migrations.py migrations/1069_remote_control.sql --stop-on-error`. Den opretter nye tabeller og ændrer ikke eksisterende sager eller tidsregistreringer.
2. Konfigurér `REMOTE_LOOKUP_KEY` med en særskilt, kryptografisk tilfældig hemmelighed på mindst 32 tegn i serverens miljø. Gem ikke nøglen i Git. Nøglen beskytter kodeopslag, PIN og anonymiserede loginbegrænsninger. Udskiftning kræver nye koder/PIN til brugerne.
3. Sæt `REMOTE_ENABLED=true` og genstart applikationen. Hold test og produktion adskilt. Fjernbetjeningen bruger samme server og port som Hub: `http://172.16.31.183:8001/remote`. HTTP understøttes til lokalnet/VPN; der kræves ingen separat Docker eller port.
4. Hvis der bruges reverse proxy, skal den bevare Host og videregive korrekt scheme. Uvicorn må kun stole på forwarded headers fra den konkrete proxy. Begræns netværksadgang til lokalnet/VPN i eksisterende firewall/proxy; modulet åbner ingen internetforbindelse eller ekstern tunnel.
5. Pilotbrugeren logger normalt ind i Hub, vælger **Fjernbetjening**, opretter en 4-cifret PIN og en navngivet fjernbetjening. Gem den 12-cifrede kode, som kun vises ved oprettelse/udskiftning. Koden bevarer indledende nuller.
6. Åbn `/remote` på mobil/tablet. Forbind med kode + PIN. Enhedens forbindelse udløber efter 30 dage; PIN kræves igen hver ny kalenderdag i København. Tilføj siden til hjemmeskærmen via browserens menu; ingen offlinehandlinger eller service-worker-cache anvendes.
7. I én Hub-fane vælges **Følg i denne fane**. En ny valgt fane overtager. Mobilen virker også uden en computer. Der synkroniseres hvert tredje sekund på synlige sider.
## Adfærd
- Timer, pause, genoptagelse, stop og manuel tid bruger de eksisterende handlers og regler. Stop kræver beskrivelse. Manuel tid gemmes på den valgte sag med dagens danske dato og automatisk afregningsvalg som Hubs hurtigregistrering. Der godkendes eller faktureres ikke automatisk fra fjernbetjeningen.
- Dagens tid tæller gemte faktiske minutter; kørende og pausede timere tælles ikke med.
- **Nye** betyder status `Ny`. **Mine** bruger Hubs konfiguration af lukkede statusser. Søgning understøtter nummer, titel og kundenavn; visningen er pagineret med 50 sager.
- Deadline vises med lokal tidszone: gul ved højst 60 minutter tilbage, rød efter overskridelse. En deadline uden offset fortolkes i `Europe/Copenhagen`. Ingen SLA-arbejdstidsregler beregnes.
- Almindelige beskeder markeres kun som læst på brugerens tryk. Beskeder med krav om bekræftelse bruger den eksisterende separate bekræftelsesfunktion.
- Ved forbindelsesbrud fortsætter serverens timer, mens mobilens handlinger deaktiveres. Ved tabt svar kan brugeren kontrollere resultatet eller eksplicit prøve samme handling igen med samme idempotensnøgle. Intet afspilles automatisk efter genforbindelse.
- Hvis Hub ændres samtidig, returneres konflikt, og mobilen henter aktuel tilstand. Sagsvalg til en afbrudt computer leveres ikke senere. En fane med igangværende redigering spørger før navigation.
## Sikkerhed og API
`/api/v1/remote` har en separat enhedscookie og giver aldrig almindelig Hub-/administratorlogin. Administration og faneforbindelse kræver almindeligt Hub-login. Alle kald kræver featureflag; cookies er Secure ved HTTPS og kan bruges over HTTP på lokalnettet. HTTP krypterer ikke kode, PIN eller session under transport. Alle skrivninger kræver samme Origin og CSRF-token fra `/bootstrap`.
- `GET /bootstrap`, `POST /connect`, `/unlock`, `/logout`: forbindelse og daglig oplåsning.
- `GET /state`, `/cases`, `/command-result/{request_id}` og `POST /command`: begrænsede remote-funktioner. Kommandoer kræver UUID, remote-version og timerfingeraftryk. Ukendte felter afvises.
- `GET /admin`, `PUT /admin/pin`, `POST /admin/remotes`, `POST /admin/remotes/{id}/rotate`, `DELETE /admin/remotes/{id}`, `DELETE /admin/devices/{id}`: kun ejerens administration.
- `POST /follow`: valgt Hub-fane, heartbeat og kortvarigt sagsvalg.
Loginforsøg reserverer 30 sekunder i PostgreSQL før legitimationskontrollen. Begrænsningen deles mellem processer og gælder klient-IP samt kendt bruger/enhed. Forkerte forsøg rulles ikke tilbage. PIN hashes med nøglebeskyttet forbehandling; koder hashes separat. Sessiontokens hashes og gemmes i Secure/HttpOnly/SameSite=Strict-cookies. Rettigheder og brugerens aktive status kontrolleres løbende. Alle remote-svar har `Cache-Control: no-store`.
## Test
Enhedstest: `./.venv/bin/python -m pytest tests/test_remote_control.py -q`.
Databaseintegration: `BMC_REMOTE_DB_TESTS=1 ./.venv/bin/python -m pytest tests/test_remote_control.py -q`. Kræver lokal PostgreSQL med eksisterende tidsmodul-schema; testen opretter og fjerner kun sit eget tilfældigt navngivne schema. Det offentlige schema ændres ikke. Tests dækker loginbegrænsning med samtidige forbindelser, sessionsadskillelse, PIN, tilbagekaldelse, timere, idempotens, konflikter, sagsfiltre og valgt fane.
Hubs databasepool er gjort trådsikker uden ændring af forbindelsesgrænser eller API-kontrakter, så samtidige synkrone remote- og Hub-kald kan bruge den sikkert.
Browser: `BMC_PLAYWRIGHT_MODULE=/path/to/playwright node tests/browser/remote_control.cjs`. Brug `BMC_REMOTE_BROWSER=webkit` for Safari/WebKit-motoren. Tests bruger udelukkende mockdata og mobil/tabletstørrelser. En fysisk iPhone/iPad-pilot skal desuden kontrollere lokalnet/VPN og Tilføj til hjemmeskærm.
Verificeret lokalt 13. oktober 2026: 23 remote-tests med isoleret PostgreSQL og 37 eksisterende timer-/registrerings-/afregningstests bestod. Regressionstestene blev kørt med `WORKFLOWS_ENABLED=false WORKFLOWS_MODE=shadow`, som de eksisterende tests forudsætter. Chrome og Safari/WebKit bestod mobil (390 px), tablet (820 px), administration og synkronisering med kun den valgte Hub-fane. JavaScript/Python-syntaks og `git diff --check` bestod. Produktionsaktivering og fysisk enhedstest over VPN er ikke udført.
## Tilbagerulning
Sæt `REMOTE_ENABLED=false` og genstart. Remote-ruter returnerer 404, og eksisterende Hub-adfærd fortsætter. Bevar de additive tabeller og alle tidsregistreringer. Hvis forbindelser skal være permanent tilbagekaldt før en senere genaktivering, tilbagekald dem i administrationen før deaktivering eller sæt `revoked=true` på remote-enhederne gennem en særskilt, godkendt driftsændring. Slet ikke sager eller tidsregistreringer.
## Kontakter og VoIP
Fjernbetjeningen kan søge aktive kontakter efter navn, firma, telefon og e-mail med Hub'ens eksisterende kontaktopslag. Det kræver `customers.view` (eller superadministrator), samme adgang som kundeoplysninger. Numrene vælges fra kontaktens telefon/mobil; fjernbetjeningen kan ikke vælge andre brugeres telefon eller angive en Action URL.
Ring-knappen bruger Hub'ens eksisterende click-to-call med den tilknyttede brugers VoIP-/Yealink-telefon og gældende netværkskontrol. Telefonopsætning og click-to-call skal være aktiveret i Hub. Samtalens lyd foregår på VoIP-telefonen, ikke i mobilbrowseren. Remote returnerer aldrig Action URL eller telefonens adgangsoplysninger.
Indgående ringende opkald vises fra Mission Controls fælles opkaldstilstand med kontakt, firma, nummer og modtagende telefoner. Visningen opdateres hvert tredje sekund, mens siden er synlig; forældede opkald skjules efter den eksisterende ringegrænse. Det er Hub'ens fælles indgående visning. En fejl i telefonivisningen stopper ikke timeren.
Opkald får en idempotensnøgle, der reserveres før kommandoen sendes til telefonen. Gentagelse med samme nøgle ringer ikke igen. Ved timeout eller tabt svar kan brugeren læse status og kontrollere telefonen; et gammelt opkald sendes aldrig automatisk efter genforbindelse. Et udfald efter reservation kan efterlade ukendt status, fordi telefonens eksterne handling ikke kan indgå i databasens transaktion.