Eigene Kontodaten exportieren (Datenportabilitaet) #158

Closed
opened 2026-09-04 12:32:32 +02:00 by lena · 2 comments
Collaborator

Story: Eigene Kontodaten exportieren (Datenportabilitaet)

As a Nutzer,
I want to meine eigenen Kontodaten (Profil, meine Listen-Beteiligung) als Datei herunterladen
koennen,
so that ich eine Kopie meiner Daten habe - z. B. bevor ich mein Konto loesche, oder einfach um
sie zu behalten.

Background

DeleteCurrentUserAccountCommand (#59) implementiert bereits das Recht auf Loeschung; ein
Gegenstueck fuer Datenuebertragbarkeit (Recht auf Auskunft/Export) existiert noch nicht. Ein
listenweiter CSV-Export existiert bereits (#83), aber kein kontoweiter Export ueber alle eigenen
Listen hinweg.

Acceptance criteria:

  • Im Account-Settings-Bereich gibt es eine Aktion "Meine Daten exportieren".
  • Der Export enthaelt mindestens: Profildaten (Name, Benutzername, E-Mail), alle Listen, in denen
    der Nutzer Owner oder Mitglied ist (Titel, enthaltene Eintraege inkl. Titel/Status), sowie vom
    Nutzer selbst verfasste Kommentare.
  • Format: strukturierte, lesbare Datei (z. B. JSON), analog zum bestehenden CSV-Export (#83),
    aber kontoweit statt pro Liste.
  • Der Export laeuft synchron im Request (kein Hintergrundjob/E-Mail-Versand noetig, sofern die
    Datenmenge das zulaesst).

Out of scope for this story:

  • Export im Namen eines anderen Nutzers.
  • Automatisierter/wiederkehrender Export oder Export als PDF.

Open questions: (escalate to human if unanswered)

  • Sollen auf gemeinsamen Listen auch von anderen Mitgliedern erzeugte Daten (z. B. deren
    Kommentare) enthalten sein, oder nur die eigenen?
## Story: Eigene Kontodaten exportieren (Datenportabilitaet) **As a** Nutzer, **I want to** meine eigenen Kontodaten (Profil, meine Listen-Beteiligung) als Datei herunterladen koennen, **so that** ich eine Kopie meiner Daten habe - z. B. bevor ich mein Konto loesche, oder einfach um sie zu behalten. ## Background `DeleteCurrentUserAccountCommand` (`#59`) implementiert bereits das Recht auf Loeschung; ein Gegenstueck fuer Datenuebertragbarkeit (Recht auf Auskunft/Export) existiert noch nicht. Ein listenweiter CSV-Export existiert bereits (`#83`), aber kein kontoweiter Export ueber alle eigenen Listen hinweg. **Acceptance criteria:** - [ ] Im Account-Settings-Bereich gibt es eine Aktion "Meine Daten exportieren". - [ ] Der Export enthaelt mindestens: Profildaten (Name, Benutzername, E-Mail), alle Listen, in denen der Nutzer Owner oder Mitglied ist (Titel, enthaltene Eintraege inkl. Titel/Status), sowie vom Nutzer selbst verfasste Kommentare. - [ ] Format: strukturierte, lesbare Datei (z. B. JSON), analog zum bestehenden CSV-Export (`#83`), aber kontoweit statt pro Liste. - [ ] Der Export laeuft synchron im Request (kein Hintergrundjob/E-Mail-Versand noetig, sofern die Datenmenge das zulaesst). **Out of scope for this story:** - Export im Namen eines anderen Nutzers. - Automatisierter/wiederkehrender Export oder Export als PDF. **Open questions:** (escalate to human if unanswered) - Sollen auf gemeinsamen Listen auch von *anderen* Mitgliedern erzeugte Daten (z. B. deren Kommentare) enthalten sein, oder nur die eigenen?
lena self-assigned this 2026-09-04 17:41:39 +02:00
Author
Collaborator

Claimed. Starting implementation.

Decision on the open question (whether shared-list data authored by other members should be included): only the requesting user's own data. Reasoning: this is framed as a data-portability/right-of-access export (GDPR Art. 20 territory), which scopes to the data subject's own personal data, not third parties' - including e.g. another member's comment text in a downloadable file handed to this user would be a mini data-exposure/GDPR problem in the other direction. For shared lists the export includes the list itself (title, entries/status - already visible to any member) and this user's own comments on it, but not other members' comments or their personal identifiers beyond what's already needed to show list membership context.

Planned shape (pending final design after a quick codebase check): a new synchronous query/download endpoint mirroring the existing per-list CSV export's (#83) response mechanism, returning one structured JSON document per request: profile (name, username, email), then one section per list type (Todo/Shopping/Pantry/MasterPacking) covering every list the user owns or is a member of, each with its entries and this user's own comments on that list. No new tables - purely a read/aggregation feature composing existing per-type "get my lists" queries where they exist.

Claimed. Starting implementation. Decision on the open question (whether shared-list data authored by other members should be included): only the requesting user's own data. Reasoning: this is framed as a data-portability/right-of-access export (GDPR Art. 20 territory), which scopes to the data subject's own personal data, not third parties' - including e.g. another member's comment text in a downloadable file handed to this user would be a mini data-exposure/GDPR problem in the other direction. For shared lists the export includes the list itself (title, entries/status - already visible to any member) and this user's own comments on it, but not other members' comments or their personal identifiers beyond what's already needed to show list membership context. Planned shape (pending final design after a quick codebase check): a new synchronous query/download endpoint mirroring the existing per-list CSV export's (#83) response mechanism, returning one structured JSON document per request: profile (name, username, email), then one section per list type (Todo/Shopping/Pantry/MasterPacking) covering every list the user owns or is a member of, each with its entries and this user's own comments on that list. No new tables - purely a read/aggregation feature composing existing per-type "get my lists" queries where they exist.
lena referenced this issue from a commit 2026-09-04 18:17:03 +02:00
Author
Collaborator

Done. Backend commit 48e8e94, frontend commit 3580fee, memory notes 76ef8c7.

Scope delivered:

  • New ExportCurrentUserDataQuery (read-only, synchronous): profile (name, username, email) plus every list the requesting user owns or is a member of across all four list types (Todo, Shopping, Pantry, MasterPacking), each with entry title/status and the user's own comments on that list.
  • Pantry export follows #94's existing rule: no membership table of its own, included whenever the linked Shopping List is.
  • "Export my data" action added to Account Settings > Security, next to (but separate from, since it's non-destructive) the danger zone. Downloads the result as a JSON file client-side - the existing per-list CSV export (#83) turned out to be 100% client-side too (no backend file-download endpoint exists anywhere in this codebase), so this follows the same convention: the backend returns plain JSON like every other CQS request, and the frontend builds the downloadable file. Extracted the shared Blob/anchor download mechanics into utils/download.ts, refactoring the CSV export to reuse it instead of duplicating.

Decision on the issue's own open question (whether other members' comments on shared lists should be included): no - only the requesting user's own comments, enforced at the query level (AuthorUserId == userId) in all three comment queries, not just left out of the DTO shape. Reasoning: this is a GDPR Art. 20-style data-portability export, which scopes to the data subject's own personal data, not third parties'.

Every entry/comment query is scoped to an id set derived from the user's own membership rows (never an unfiltered table scan) - traced end-to-end by a dedicated security-review subagent, which found no cross-user leakage path.

Tests: 6 new backend tests (profile export; todo/shopping/pantry/master-packing list export with entries; a list the user isn't a member of never appearing; own-comments-only enforcement for Todo, Pantry, and MasterPacking) plus 2 new frontend tests (successful export+download, and the error path).

Verification: backend dotnet build/dotnet test clean (Docker unavailable in this environment the whole cycle - confirmed the new tests fail with the same DockerUnavailableException shape as every other DB-backed test here, not a setup/DI error); frontend npm run build and full vitest suite green (125 files / 1153 tests). Real CI was pending on the whole matrix throughout the cycle (known runner-congestion pattern) - not yet confirmed green on the actual pipeline as of this comment.

Done. Backend commit 48e8e94, frontend commit 3580fee, memory notes 76ef8c7. Scope delivered: - New `ExportCurrentUserDataQuery` (read-only, synchronous): profile (name, username, email) plus every list the requesting user owns or is a member of across all four list types (Todo, Shopping, Pantry, MasterPacking), each with entry title/status and the user's own comments on that list. - Pantry export follows #94's existing rule: no membership table of its own, included whenever the linked Shopping List is. - "Export my data" action added to Account Settings > Security, next to (but separate from, since it's non-destructive) the danger zone. Downloads the result as a JSON file client-side - the existing per-list CSV export (#83) turned out to be 100% client-side too (no backend file-download endpoint exists anywhere in this codebase), so this follows the same convention: the backend returns plain JSON like every other CQS request, and the frontend builds the downloadable file. Extracted the shared Blob/anchor download mechanics into `utils/download.ts`, refactoring the CSV export to reuse it instead of duplicating. Decision on the issue's own open question (whether other members' comments on shared lists should be included): no - only the requesting user's own comments, enforced at the query level (`AuthorUserId == userId`) in all three comment queries, not just left out of the DTO shape. Reasoning: this is a GDPR Art. 20-style data-portability export, which scopes to the data subject's own personal data, not third parties'. Every entry/comment query is scoped to an id set derived from the user's own membership rows (never an unfiltered table scan) - traced end-to-end by a dedicated security-review subagent, which found no cross-user leakage path. Tests: 6 new backend tests (profile export; todo/shopping/pantry/master-packing list export with entries; a list the user isn't a member of never appearing; own-comments-only enforcement for Todo, Pantry, and MasterPacking) plus 2 new frontend tests (successful export+download, and the error path). Verification: backend `dotnet build`/`dotnet test` clean (Docker unavailable in this environment the whole cycle - confirmed the new tests fail with the same DockerUnavailableException shape as every other DB-backed test here, not a setup/DI error); frontend `npm run build` and full vitest suite green (125 files / 1153 tests). Real CI was `pending` on the whole matrix throughout the cycle (known runner-congestion pattern) - not yet confirmed green on the actual pipeline as of this comment.
lena closed this issue 2026-09-04 18:17:36 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
robert/todo#158
No description provided.