feat(web): EYT-113 costs.read-Ladegrenze an der ausgewaehlten Organisation - #99
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ehlten Organisation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Loeschung bewiesen Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ach Freigabe Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Gate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ie Messung angepasst Die Suite mockt das Freigabemodul als Ganzes; die ausgefuehrte Lib-Mutation M3 (unbekannt wie abgemeldet) liess sie messbar gruen. Die Kommentare nennen jetzt die Mutationsebene, die diese Suite wirklich rot macht (KostenGrenze), und verweisen fuer die Lib-Ebene auf kosten-freigabe.test.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reviewer's GuideDie Kostenoberflächen werden nun vor serverseitiger und clientseitiger Kostenladung organisationsbezogen geschützt: Sequence diagram for the organisation-scoped costs access flowsequenceDiagram
participant User
participant SessionProvider
participant Providers
participant ServerPage
participant KostenFreigabe
participant KostenFlaeche
participant KostenAPI
User->>SessionProvider: selectOption
SessionProvider->>Providers: onOrganisationChange
Providers->>Providers: schreibeOrgAuswahl
Providers->>ServerPage: router.refresh
ServerPage->>KostenFreigabe: leseKostenFreigabe
KostenFreigabe->>KostenFreigabe: leseServerSitzung
KostenFreigabe->>KostenFreigabe: kostenFreigabe
alt selected organisation has costs.read
KostenFreigabe-->>ServerPage: gewaehert
ServerPage-->>User: render KostenFlaeche
KostenFlaeche->>KostenAPI: request costs data
else missing permission or invalid selection
KostenFreigabe-->>ServerPage: verboten or keine-auswahl
ServerPage-->>User: render KostenGrenze
end
Flow diagram for fail-closed organisation-scoped costs authorizationflowchart TD
Start[Kosten route request] --> Gate[leseKostenFreigabe]
Gate --> SessionState{Session state}
SessionState -->|unbekannt| Unknown[kosten-sitzung-unbekannt]
SessionState -->|abgemeldet| Unauthenticated[kosten-unauthenticated]
SessionState -->|authenticated| Selection{Valid selected organisation?}
Selection -->|no| Choose[kosten-org-auswahl]
Selection -->|yes| Permission{Selected organisation has costs.read?}
Permission -->|no| Forbidden[kosten-forbidden]
Permission -->|yes| Granted[Render kosten-free loader]
Granted --> Lazy[Load costs client chunks]
Unknown --> End[No costs chunks]
Unauthenticated --> End
Choose --> End
Forbidden --> End
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…u werfen decodeURIComponent warf auf boesartiger/kaputter Prozentkodierung (eyt_org=%) URIError — und die Kompositionswurzel liest den Selector beim Client-Start, der Crash traefe jede Seite. Jetzt: kaputte Kodierung ergibt null (keine Auswahl), kein Rueckfall, keine Normalisierung, keine Ausnahme. TDD: Unit-Rot gegen die alte Implementierung gemessen (URIError), dann gruen; Journey-Phase h beweist im echten Browser pageerror=0, kosten-org-auswahl, keine neuen Kosten-Chunk-Anfragen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
EYT-113 Inkrement 2 — costs.read-Ladegrenze an der ausgewählten Organisation
Schließt den offenen EYT-113-Akzeptanzpunkt: Kosten-Navigation und Kostenkomponenten werden nur nach verifiziertem
costs.readgeladen; serverseitige Autorisierung bleibt verbindlich — für/kostenund/kosten/stundensaetze, einschließlich Mehr-Organisation-Benutzern.PO-Entscheidung umgesetzt: Kostenberechtigung bleibt organisationsbezogen; die Abschwächung „
costs.readin irgendeiner Organisation" ist verboten und per ausgeführter Gegenmutation als rot bewiesen. Die ausgewählte Organisations-Id ist ausschließlich Selector, nie Autorisierungsquelle.Basis und Umfang
028ccfb39f54db52d74b15b88434357371d39265(= origin/master bei Brancherstellung und beim Push, frisch nachgemessen)feat/eyt-113-selected-org-cost-boundary, Head:c174f127beb0937a899bd88206e5455ab6aa24703ad9fda…c174f12), TDD-Historie: rote Beweise committet vor der Implementierung (d3ff4e1)Implementierungsentscheidung
eyt_org(lib/organisations-auswahl-cookie.ts): bewusst sichtbar (nicht HttpOnly, kein Geheimnis, kein Token; Session-Lebensdauer,SameSite=Lax). Geschrieben an genau einer Stelle (Kompositionswurzelapp/providers.tsxim bestehendenonOrganisationChange-Seam, mitrouter.refresh()bei Wertänderung); beim Logout gelöscht (per Journey-Zusicherung bewiesen). Der Server verifiziert die Id gegen die real geprüfte Session (GET /auth/session): fremde/ungültige Id, fehlende Auswahl, fehlendes Recht, unbekannte Session ⇒ fail closed (lib/kosten-freigabe.ts, pure Matrix mit 9 Fällen).leseKostenFreigabe()als erstes Statement; Verweigerung rendert die Server-Flächen incomponents/kosten-grenze.tsx(kosten-forbidden/kosten-unauthenticated/kosten-org-auswahl/ neukosten-sitzung-unbekannt);headers()/cookies()machen beide Routen dynamisch (vorher war/kosten/stundensaetzestatisch vorgerendert — bewusste Änderung; der Build braucht weiterhin keinEASYTREE_API_PROXY_TARGET, mitenv -u … pnpm build --forcebelegt)./_next/static/chunks/16y54lugq9hok.jstrotz Server-Verweigerung.await import()im gewährten Zweig): widerlegt — Next 16.2.11/Turbopack schreibt die komplette Client-Reference-Chunkliste der Route als unbedingte<script async>-Tags in den Denial-HTML-Head (direkt am ausgelieferten HTML gemessen); ein server-seitiger Dynamic-Import verschiebt das Modul-Laden, nicht den Manifest-Eintrag.components/kosten-flaeche.tsx,components/stundensaetze-flaeche.tsx,next/dynamic);KostenZugang/KostenAnsicht/RateManagementsind Lazy-Chunks außerhalb der Route-Manifeste und laden erst, wenn der Server die Freigabe erteilt hat.lib/feld/sitzung-server.ts→lib/sitzung-server.ts(reiner Move, Rename-Diff = exakt die eine interne Importzeile, 97 % Similarity; drei Importeure nachgezogen).orgs.findgegen die Session);SessionProvidermeldet die Auswahl nur noch in den Zuständenangemeldet/abgemeldetnach außen (ein transienterlaedt/fehler-Nullwert löschte sonst das Cookie).Organisations-/Permission-Matrix (jede Zeile real gemessen)
costs.readangefordertnon-leer)costs.read(member)kosten-forbiddenserverseitig, 0 Kosten-Chunk-Requests auf beiden Routen, Chunk-Dateinamen nicht im Dokument, keine Beträgezusammenfassung-feld.json:ergebnis: passed)kosten-org-auswahl, 0 Chunk-Requestskosten-forbidden, 0 Chunk-Requests — das Recht der anderen Org überschreibt nichtsdecision:deny, reason:PERMISSION_MISSING(Phase d, 403 — der richtige Hebel, nichtORG_NOT_A_MEMBER)/kosten/stundensaetzeim selben Zustandrouter.refresh-Pfad)context.addCookies)kosten-org-auswahl, keine neuen Chunk-Requestskosten-unauthenticated(Banner, kein neuer Redirect-Fluss)kosten-sitzung-unbekannt, ausdrücklich ≠ abgemeldet, kein Kosten-ChunkLoad-/RSC-/Chunk-Evidenz (Formen laut Auftrag)
page_client-reference-manifest.jsbeider Kosten-Routen enthalten 0 Treffer fürkosten-ansicht|kosten-zugang|rate-management(nur die Loader) — vorher beide Routen voll referenziert.[]Kosten-Chunk-Requests (Marker-abgeleitete Chunk-Menge zur Laufzeit aus.next/static/chunks, Helper wirft bei leerer Menge); Owner/gewährt["0rp90oxbbgao1.js","13psdmgzzclg2.js"].page.content()der verweigerten Routen; Stufe-2-Widerlegung direkt am Denial-HTML (<script src=".../3liqjy_g2e8wn.js" async>im Head) gemessen.ƒ /kosten,○ /kosten/stundensaetze→ nachher beideƒ;prerender-manifest.jsonohne Kosten-Routen.TDD: vorher roter Test
kosten-page-gate.test.tsx: 18 failed | 2 passed vor der Implementierung (Pages riefenleseKostenFreigabe0-mal; Denial-Testids fehlten; Client-Wächter montierte). Committet alsd3ff4e1, grün ab8170bbb(20/20).16y54lugq9hok.jsauf/kostenan (zugleich die Reproduktionsmessung des fehlenden Gates).Gegenmutationen (alle ausgeführt: rot → restauriert → grün; Rücknahme je mit leerem, eingegrenztem
git diffbelegt)stundensaetze/page.tsx, der geforderte Gegenbeweis): Architekturregel rot am echten Baum (Kanarie der neuen Regelkosten-server-gate), Unit 9 rot, Journey rot (/kosten/stundensaetze: angeforderte Kosten-Chunkserwartete[], erhielt["0rp90oxbbgao1.js"]) — nach Restore Journey 5 passed.kosten-freigabe.ts): Fall 8 rot (fremder Selector fiele sonst auf die berechtigte Org zurück).kosten-freigabe.ts):kosten-freigabe.test.tsrot. Befund: die Page-Gate-Suite blieb dabei grün (sie mockt das Freigabemodul) — ihr Kommentar behauptete das Gegenteil und wurde inc174f12an die Messung angepasst (Mutationsebene KostenGrenze vs. Lib jetzt korrekt benannt).session-provider.tsx): Fall (a) rot (Cookie-Löschung währendlaedt).kosten-server-gate(9b46515): über den aufgelösten Import-Graphen (kein reiner Stringvergleich), Nicht-Leerlauf „≥ 2 Kosten-Pages gesehen" als eigene Violation, Rot-Beweis gegen synthetischen Baum inos.tmpdir()und Kanarie am echten Baum (M1).Bewusste Änderung einer bestehenden Zusicherung
Journey-Schritt 11 („kein Token in Browserspeicher") prüfte pauschal
document.cookie === ""— mit einem client-geschriebenen Selector-Cookie per Konstruktion unvereinbar. Geschärft statt abgeschwächt (5f88862): sichtbare Cookie-Namensmenge ⊆ {eyt_org} (jeder neue sichtbare Cookie macht den Schritt rot — strenger als vorher), Wert exakt die Fixture-Org-UUID, expliziter Token-Form-Wächter (kein.), localStorage/sessionStorage/DOM-Prüfungen unverändert; zusätzlich neu: Logout löschteyt_org(beweist den Clear-Zweig).Validierung (Evidenzlevel: lokal ausgeführt; CI auf diesem Head folgt als Required-Checks-Lauf)
turbo run lint|typecheck|test --force: jeCached: 0 cached, 6/10/10 Tasks; Tests: config 36, contracts 201, ui 91, domain 246, web 449 (38 Dateien), api 821 (+95 skipped = exakt die 13mode=local-Integrationsgates bei gestopptem Stack — deren Pflichtbeweis ist der CI-Jobdb-gates)env -u EASYTREE_API_PROXY_TARGET pnpm build --force: exit 0,Cached: 0 cacheddist/main.js+ GoTrue + Supabase): 5 passed, mehrfach reproduziert (auch nach M1-Restore)/kosten-Smokes (Render/axe/Tokens) grün auf der neuen Unknown-Flächeeyt113-zweitorg-an/aus.sql: Prämissenwächter (A genau 1 Mitgliedschaft;('member','costs.read')nicht inrole_permissions; keine Restbestände) + Nachbedingungen, Marker[eyt113-zweitorg-an] leihe=1 a_aktiv=2 a_zweitorg_costs_read=f a_reiseorg_costs_read=t/[eyt113-zweitorg-aus] leihe=0 org=0 a_aktiv=1 owner_aktiv=1; Teardownrestzeilen=0offeneSitzung), kein TODO/FIXME/TOOL_GAPScreenshot-Artefakte (Playwright-Testartefakte,
test-results/auth-journey/artefakte/…)eyt113-kosten-positiv.png(Owner, gewährte Kostenfläche)eyt113-kosten-forbidden.png(Member, Server-Forbidden)eyt113-mehrorg-forbidden.png(Mehr-Org, gewählte Org ohne Recht)Nicht enthalten / unverändert
Keine Migration, keine neuen API-Endpunkte, keine Änderung an
MembershipCostAccessPolicyoder RLS, keinNEXT_PUBLIC_*, kein Eingriff in EYT-80/81/147/148. Rollback:git revertder Branch-Commits stellt den Vorzustand vollständig her; Feld-Verhalten per Ein-Zeilen-Rename-Diff + grünen Feld-Tests unberührt.🤖 Generated with Claude Code
Summary by Sourcery
Enforce selected-organisation cost authorization and prevent unauthorized cost content from being loaded on either cost route.
New Features:
costs.readauthorization for both cost routes using a verified organisation selector, with fail-closed handling for missing, invalid, or unknown sessions.Bug Fixes:
Enhancements:
Tests: