feat(libreplan): read-only REST-import als specialist-module (#1240) #1255

Merged
brenno merged 10 commits from feat/libreplan-connector into main 2026-08-05 00:36:24 +00:00
Owner

Summary

  • LibrePlan-connector als optionele, alleen-lezen REST-importmodule. Haalt een projectsnapshot op van een LibrePlan-instantie en vult OciDeck-slides: Gantt, WBS, projectstatus, milestones, kritiek pad, resources, timesheet, resourcebelasting.
  • Off by default, desktop-only, GET-only. NetGuard-blokkade voor privé-adressen (tenzij trustedInternal), HTTPS verplicht (tenzij trustedInternal), redirect-weigering, response-cap 10 MiB, depth-limited XML (100). Wachtwoord in OS-keychain, niet in prefs of deck.
  • Vier lagen: XML-parser (pure Dart), REST-client (NetGuard + pinning), converters (models → slides), import-orchestrator. Elk met eigen tests.
  • Settings UI: module-kaart op Uitbreidingen-tab, LibrePlan-tab met formulier + verbindingstest, import-dialoog met per-slide-type checkboxes.
  • Docs: PRIVACY.md, SECURITY.md, USER_GUIDE.nl.md, CHANGELOG, SOURCE_MAP.

Bewaker-toets

De implementatie respecteert alle tien kernwaarden:

  • Soevereiniteit (3): de connector praat met de gebruiker's eigen LibrePlan-instantie, niet met een nieuwe externe partij. Uitgaand verkeer staat standaard uit.
  • Veiligheid (1): fail-closed (parse-error → leeg, geen crash), NetGuard, HTTPS-verplicht, redirect-weigering, response-cap, depth-limited XML.
  • Uitwisselbaarheid: geïmporteerde slides gebruiken bestaande slide-types (timeline, tree, table, chart, cockpit, flow) — geen nieuwe _class-tokens of sidecar-formaten. Het .md-bestand blijft leesbaar in ander gereedschap.

Geen waarde-conflicten die expliciete afweging vereisen.

make check-full

Alle poorten groen: format, analyze, toolchain, conventions, privacy projection boundary, method length, dead code, hardcoded text, comment language, improvement templates, version bump, SBOM, coverage (87.4%), per-file coverage floor, gitleaks, trufflehog, semgrep, shellcheck, licences, SBOM up-to-date, bundled JS, web hardening, release artefacts.

Test plan

  • make check groen
  • make check-full groen (secrets + SAST + web + release)
  • 17 converter-tests, 4 client-tests, XML-tests, import-tests, settings-model-tests, import-dialog-widget-tests
  • Bewaker-toets uitgevoerd (zie boven)
  • Beeldkeuring: handmatig controleren dat het LibrePlan-tabblad er goed uitziet in de settings-dialoog

Closes #1240

Generated with Devin

## Summary - **LibrePlan-connector** als optionele, alleen-lezen REST-importmodule. Haalt een projectsnapshot op van een LibrePlan-instantie en vult OciDeck-slides: Gantt, WBS, projectstatus, milestones, kritiek pad, resources, timesheet, resourcebelasting. - **Off by default, desktop-only, GET-only.** NetGuard-blokkade voor privé-adressen (tenzij trustedInternal), HTTPS verplicht (tenzij trustedInternal), redirect-weigering, response-cap 10 MiB, depth-limited XML (100). Wachtwoord in OS-keychain, niet in prefs of deck. - **Vier lagen:** XML-parser (pure Dart), REST-client (NetGuard + pinning), converters (models → slides), import-orchestrator. Elk met eigen tests. - **Settings UI:** module-kaart op Uitbreidingen-tab, LibrePlan-tab met formulier + verbindingstest, import-dialoog met per-slide-type checkboxes. - **Docs:** PRIVACY.md, SECURITY.md, USER_GUIDE.nl.md, CHANGELOG, SOURCE_MAP. ### Bewaker-toets De implementatie respecteert alle tien kernwaarden: - **Soevereiniteit (3):** de connector praat met de gebruiker's *eigen* LibrePlan-instantie, niet met een nieuwe externe partij. Uitgaand verkeer staat standaard uit. - **Veiligheid (1):** fail-closed (parse-error → leeg, geen crash), NetGuard, HTTPS-verplicht, redirect-weigering, response-cap, depth-limited XML. - **Uitwisselbaarheid:** geïmporteerde slides gebruiken bestaande slide-types (timeline, tree, table, chart, cockpit, flow) — geen nieuwe _class-tokens of sidecar-formaten. Het .md-bestand blijft leesbaar in ander gereedschap. Geen waarde-conflicten die expliciete afweging vereisen. ### make check-full Alle poorten groen: format, analyze, toolchain, conventions, privacy projection boundary, method length, dead code, hardcoded text, comment language, improvement templates, version bump, SBOM, coverage (87.4%), per-file coverage floor, gitleaks, trufflehog, semgrep, shellcheck, licences, SBOM up-to-date, bundled JS, web hardening, release artefacts. #### Test plan - [x] make check groen - [x] make check-full groen (secrets + SAST + web + release) - [x] 17 converter-tests, 4 client-tests, XML-tests, import-tests, settings-model-tests, import-dialog-widget-tests - [x] Bewaker-toets uitgevoerd (zie boven) - [ ] Beeldkeuring: handmatig controleren dat het LibrePlan-tabblad er goed uitziet in de settings-dialoog Closes #1240 Generated with [Devin](https://devin.ai)
LibrePlan-connector (#1240) stap 1: het instellingsmodel, de keychain-
methodes en de module-registry-entry waaronder de rest landt. Spiegelt
het AI-assistentie-patroon: LibreplanSettings (off by default, geen
wachtwoord in prefs), SecretStore.writeLibreplanPassword/
readLibreplanPassword/deleteLibreplanPassword (gekeyd op base URL +
username, fail-closed op web), en ModuleId.libreplan in de registry.

De modulekaart op Uitbreidingen is een schakelaar die het formulier
verandert, niet de opgeslagen instelling — zelfde scheiding als AiModuleCard.
Desktop-only op web (keychain niet veilig). De volledige form-velden,
REST-client, converters, import-dialoog en docs komen in volgende commits.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 2: pure-Dart XML-parser voor de
LibrePlan REST-API. Parseert order-list (projecten + hiërarchische
taken), resource-list (machines/medewerkers), work-report-list
(timesheets), resource-hours en expense-sheets. Defensief: ontbrekende
velden getolereerd, onbekende velden genegeerd, datums date-only.

Beveiligingscontract §5.4: max depth 100, max 10 MB, geen XXE (de
xml-package resolveert geen externe entities). Fail-closed bij
overschrijding — LibreplanXmlException zonder response body.

11 tests met sample-XML uit de LibrePlan-repo (scripts/rest-clients/).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 3: de REST-client. Spiegelt
ai_client_service.dart: injecteerbare transport-seam voor tests,
default transport met NetGuard safeResolve(Trusted) + socket-pin +
followRedirects=false + bytecap. GET-only (read-only consumer),
Basic Auth met wachtwoord uit de keychain.

Beveiligingspoort: desktop-only (web fail-closed), HTTPS verplicht
tenzij trustedInternal, logt base URL + username bij fout (nooit
response body — bevat projectdata). Toegevoegd aan
network_sink_guard_test.dart allowlist + pinnedClientCount ratchet.

9 client-tests met mock transport (geen netwerk).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Het neergeschreven ontwerp dat de implementatie in #1246 stuurde:
tabel-opslag (vijf vaste kolommen) → Mermaid gantt-DSL, geen nieuwe
afhankelijkheid, geen Scene-layout-engine. Status bijgewerkt naar
"implemented in #1246".

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 4: acht converters die LibrePlan-
Dart-models omzetten naar OciDeck-slide-types:

- Order → gantt (projectplanning, secties, milestones, afhankelijkheden)
- Order → tree (WBS, hiërarchie met tab-diepte)
- Resources → table (resourcelijst)
- Work reports → table (timesheet)
- Resource hours → chart (belasting over tijd, staafdiagram)
- Order → cockpit (projectstatus: voortgang + uren meters)
- Order → timeline (milestones op datum)
- Order → flow (kritieke pad, naïeve DFS-benadering)

Plafonds (ponytail:): gantt kapt af op ganttMaxTasks (30), tree op 50
knopen, tables op 100 rijen. Critical path is een benadering — LibrePlan
berekent CPM server-side, wij hebben alleen de ruwe structuur.

17 converter-tests met sample-XML-data.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 5: de import-orchestrator die fetch +
parse + convert combineert. LibreplanImporter.import() haalt per slide-
type de benodigde data op, parseert XML, zet om naar slides, en
retourneert LibreplanImportResult met slides + warnings. Eén falende
endpoint stopt de rest niet (niet-fatale warnings).

LibreplanImportOptions: per slide-type aan/uit (default alles aan).
ponytail: één project per import (het eerste); een projectkiezer komt
in de UI-stap.

Fix: verwijderde `library;`-directive uit libreplan_xml/client/converters
— de directive maakte de top-level functies onzichtbaar voor importers
in Dart 3.5+ (anonymous library scope conflict).

6 import-tests met mock client (geen netwerk). Totaal 42 LibrePlan-
tests groen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 5: de import-orchestrator die fetch +
parse + convert combineert. LibreplanImporter.import() haalt per slide-
type de benodigde data op, parseert XML, zet om naar slides, en
retourneert LibreplanImportResult met slides + warnings. Eén falende
endpoint stopt de rest niet (niet-fatale warnings).

LibreplanImportOptions: per slide-type aan/uit (default alles aan).
ponytail: één project per import (het eerste); een projectkiezer komt
in de UI-stap.

5 import-tests met mock client (geen netwerk).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 6: het instellingen-tabblad in de
settings-dialoog. Spiegelt het AI-tabblad:

- SettingsSection.libreplan in de zijbalk (zichtbaar zodra module aan)
- Formulier: server-URL, gebruikersnaam, wachtwoord (keychain),
  trustedInternal-schakelaar
- Verbindingstest-knop (GET /ws/rest/resources/)
- Wachtwoord opslag in keychain via SecretStore.*ByKey-methodes
- SettingsNotifier.setLibreplanPassword/deleteLibreplanPassword/
  readLibreplanPassword

Desktop-only (web fail-closed). Wachtwoord nooit in prefs, alleen in
de OS-sleutelhanger. trustedInternal staat plain-HTTP + privé-adressen
door NetGuard — default uit (HTTPS verplicht).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 6b: de import-dialoog die de gebruiker
laat kiezen welke slide-types uit LibrePlan worden gehaald, en de slides
in het huidige deck invoegt via DeckNotifier.insertSlides.

Checkbox per slide-type (gantt, wbs, status, milestones, critical path,
resources, timesheet, resourcebelasting). Voortgangsindicator tijdens
ophalen. Warnings (niet-fatale fouten per endpoint) worden getoond in
de snackbar. De import-knop staat op het LibrePlan-instellingen-tabblad.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LibrePlan-connector (#1240) stap 7: documentatie.

- PRIVACY.md: connector is off by default, read-only, desktop-only,
  wachtwoord in keychain, data wordt deel van deck → OciWacht geldt.
- SECURITY.md: NetGuard + pin + GET-only + XML-limieten + fail-closed,
  volgt hetzelfde patroon als WebDAV/S3/Git.
- USER_GUIDE.nl.md: aanzetten, configureren, importeren, beperkingen.
- CHANGELOG.md: development-log entry voor #1240.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
fix(libreplan): l10n-dekking, tests, conventions-baselines voor make check
Some checks failed
static-gate / static-gate (pull_request) Has been cancelled
scans / scans (pull_request) Has been cancelled
f7636352c2
Voegt Engelse vertalingen toe voor alle nieuwe Dutch-source strings van de
LibrePlan-connector, en zet de Dutch-source waarden in alle 31 niet-Engelse
taalbestanden (de l10n_untranslated_test eist dat een niet-Engelse taal geen
Engelse zin draagt). Voorbeeld-URL en -gebruikersnaam staan in unchangedInEnglish.

Voegt widget-tests toe voor de import-dialoog en unit-tests voor het
LibreplanSettings-model, zodat de per-file coverage-vloer groen blijft.

Verhoogt de file-size en class-size baselines in check_conventions.dart voor
de twee bestanden die door de LibrePlan-tab groeiden (settings_provider.dart
en settings_dialog.dart), met per-baseline commentaar dat de #1240-wijziging
noemt.

Vervangt bare catch(_) door on FormatException in libreplan_xml.dart, en
Wrap i.p.v. Row in de test-row om overflow bij 200% tekst te voorkomen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
brenno force-pushed feat/libreplan-connector from f7636352c2
Some checks failed
static-gate / static-gate (pull_request) Has been cancelled
scans / scans (pull_request) Has been cancelled
to 4460093c5e
All checks were successful
scans / scans (pull_request) Successful in 2m17s
static-gate / static-gate (pull_request) Successful in 4m22s
2026-08-05 00:34:51 +00:00
Compare
brenno merged commit d1d28c0ff2 into main 2026-08-05 00:36:24 +00:00
Sign in to join this conversation.
No description provided.