Compare commits

...
2 Commits
31 changed files with 1750 additions and 124 deletions
+115 -2
View File
@@ -1,2 +1,115 @@
% TODO: Zeitplan Soll/Ist, Backlog (PBI-Tabelle), PR-Übersicht,
% Anforderungsmatrix, Dokumententypen↔Ordner↔Icon, Lookup-Trade-offs, Beteiligte
\begin{longtable}{@{} l p{62mm} c c l @{}}
\caption{Product Backlog Items des Features 484 „Dokumente"}
\label{tab:backlog} \\
\toprule
\textbf{ID} & \textbf{Titel} & \textbf{Aufwand} & \textbf{Sprint} & \textbf{Status} \\
\midrule
\endfirsthead
\toprule
\textbf{ID} & \textbf{Titel} & \textbf{Aufwand} & \textbf{Sprint} & \textbf{Status} \\
\midrule
\endhead
\bottomrule
\endfoot
9295 & Document Explorer & 8 & 15.2026 & Dev Completed \\
9549 & Icon Typen UI & 3 & 15.2026 & Test Completed \\
9298 & Suchfunktion im Document Explorer & 3 & 15.2026 & Test Completed \\
9299 & Einzelne Dokumente herunterladen & 3 & 15.2026 & Dev Completed \\
9296 & Mehrere Dokumente als ZIP runterladen & 5 & 15.2026 & Dev Completed \\
9300 & PDF Modal Previewer & 3 & 15.2026 & Dev Completed \\
9294 & Document Link previews UI & 5 & 15.2026 & Test Completed \\
9301 & Support für URL-Dateien & 5 & 15.2026 & Dev Completed \\
\addlinespace
9749 & Nach Dokumenttypen filtern & 2 & 16.2026 & Dev Completed \\
9731 & Pagination im Document Explorer & 3 & 16.2026 & Dev Completed \\
9857 & Recherche: Anfrage Objects nach S3 optimieren & --- & 16.2026 & Dev Completed \\
9560 & Automatische Anlage der Ordnerstruktur & 3 & 16.2026 & Committed \\
10018 & Kundenordner-Lookup über Efecte-Namen & 3 & 16.2026 & Committed \\
\addlinespace
10070 & Race Conditions im Kundenordner-Lookup & \footnotesize Timebox & --- & Approved \\
10134 & \emph{Bug:} Suchfunktion wie auf den anderen Seiten & --- & 17.2026 & To Approve \\
\end{longtable}
\begin{longtable}{@{} l p{95mm} l @{}}
\caption{Zuordnung der Anforderungen zu den Product Backlog Items}
\label{tab:requirements} \\
\toprule
\textbf{Anf.} & \textbf{Kurzbeschreibung} & \textbf{PBI} \\
\midrule
\endfirsthead
\toprule
\textbf{Anf.} & \textbf{Kurzbeschreibung} & \textbf{PBI} \\
\midrule
\endhead
\bottomrule
\endfoot
FA-1 & Dokumentenliste auf \texttt{/documents} & 9295 \\
FA-2 & Mandantentrennung über \texttt{efecte-org-id} & 9295 \\
FA-3 & Rollenbasierter Zugriff, 403 ohne Berechtigung & 9295 \\
FA-4 & Typisierung und Icons & 9549 \\
FA-5 & Serverseitige Suche nach Titel & 9298 \\
FA-6 & Filter nach Dokumententyp & 9749 \\
FA-7 & Paginierung mit wählbarer Seitengröße & 9731 \\
FA-8 & Einzeldownload über Pre-Signed URL & 9299 \\
FA-9 & ZIP-Download mehrerer Dokumente & 9296 \\
FA-10 & PDF-Vorschau im Modal & 9300 \\
FA-11 & Share-Links mit OpenGraph-Vorschau & 9294 \\
FA-12 & Unterstützung von \texttt{.url}-Dateien & 9301 \\
FA-13 & Automatische Anlage der Ordnerstruktur & 9560 \\
\addlinespace
NFA-1 & Skalierbarkeit des Kundenordner-Lookups & 9857, 10018 \\
NFA-2 & Verständliche Fehlerbehandlung & 9295 \\
NFA-3 & Leerer Zustand bei fehlenden Dokumenten & 9295 \\
NFA-4 & Konsistenz zur bestehenden Oberfläche & 9731, 10134 \\
NFA-5 & Testbarkeit mit gemocktem \texttt{IAmazonS3} & 10018 \\
NFA-6 & Pfadsicherheit beim Download & 9299 \\
\end{longtable}
\begin{longtable}{@{} p{30mm} p{40mm} p{48mm} l @{}}
\caption{Bewertung der Lösungsansätze für den Kundenordner-Lookup}
\label{tab:lookup-tradeoffs} \\
\toprule
\textbf{Ansatz} & \textbf{Vorteil} & \textbf{Ausschlussgrund} & \textbf{Ergebnis} \\
\midrule
\endfirsthead
\toprule
\textbf{Ansatz} & \textbf{Vorteil} & \textbf{Ausschlussgrund} & \textbf{Ergebnis} \\
\midrule
\endhead
\bottomrule
\endfoot
In-Memory-Cache
& Folgeaufrufe kostenlos
& Pro Instanz eigener Cache; nach Neustart leer; linearer Aufwand bleibt
& verworfen \\
\addlinespace
Claim im Anmelde\-token
& Aufwand nur je Anmeldung
& Token unveränderlich, wird bei Umbenennung inkonsistent
& verworfen \\
\addlinespace
Feld in Efecte
& Löst das Problem vollständig
& Zweite Datenhaltung mit Konsistenzrisiko; Nachpflege aller Bestandskunden
& verworfen \\
\addlinespace
S3 Select
& Serverseitige Filterung
& Filtert Objektinhalte, nicht Metadaten mehrerer Objekte
& ungeeignet \\
\addlinespace
StorageGRID Search Integration
& Echte Metadatensuche; fachlich sauberste Lösung
& Vom Betreiber nicht angeboten
& nicht verfügbar \\
\addlinespace
Organisations-ID im Ordnernamen
& Technisch einfachste Lösung
& Zerstört die alphabetische Sortierung nach Kundennamen
& verworfen \\
\addlinespace
\textbf{Ableitung aus dem Firmennamen}
& Ein Aufruf im Regelfall; keine zweite Datenhaltung; selbstheilend
& Mutierender Anteil im Anfragepfad (Abschnitt~\ref{sec:race-conditions})
& \textbf{gewählt} \\
\end{longtable}
+48 -9
View File
@@ -1,13 +1,52 @@
\section{Autorisierungskonzept}
\label{sec:authorization}
% TODO: App-Rolle Documents.Read (Entra ID), Claim efecte:company_id
% Menüpunkt nur sichtbar mit Rolle, Direktzugriff ohne Rolle → 403 (nicht 404)
% Sequence-Diagramm auth-sequence.pdf
Der Dokumentenbereich verarbeitet Vertragsunterlagen, Abrechnungsdaten und Security Assessments. Ein fehlerhafter Zugriff hätte damit unmittelbar die Offenlegung vertraulicher Kundendaten zur Folge. Das Autorisierungskonzept arbeitet deshalb zweistufig: Eine Rollenprüfung entscheidet, \emph{ob} ein Benutzer den Bereich überhaupt betreten darf, und eine Mandantenprüfung entscheidet, \emph{welche} Dokumente er darin sieht.
% \begin{figure}[H]
% \centering
% \includegraphics[width=0.9\textwidth]{figures/diagrams/auth-sequence.pdf}
% \caption{Autorisierungsablauf}
% \label{fig:auth-sequence}
% \end{figure}
\subsection{Authentifizierung und Claims}
Die Authentifizierung erfolgt wie in der übrigen Houston-Anwendung über Microsoft Entra~ID. Nach erfolgreicher Anmeldung erhält Houston ein Token, das neben der Identität des Benutzers zwei für dieses Projekt wesentliche Angaben enthält:
\begin{itemize}
\item die \textbf{Efecte-Organisations-ID} des Benutzers, über die seine Zugehörigkeit zu einem Kunden bestimmt wird,
\item den \textbf{Efecte-Firmennamen}, der über den Claim \texttt{efecte:company\_name} bereitgestellt wird.
\end{itemize}
Beide Werte lagen bereits vor Projektbeginn im Token vor und mussten nicht neu eingeführt werden. Dass der Firmenname verfügbar ist, wurde erst im Rahmen der Recherche zum Kundenordner-Lookup als relevant erkannt — er ermöglicht es, den Ordnernamen ohne zusätzlichen Efecte-Aufruf zu bestimmen (siehe Abschnitt~\ref{sec:lookup-decision}).
\subsection{Anwendungsrolle Documents.Read}
Für den Zugriff auf den Dokumentenbereich wurde in Entra~ID die Anwendungsrolle \texttt{Documents.Read} angelegt. In der Feature-Analyse war ausdrücklich geklärt worden, dass keine feingranulareren Berechtigungen unterhalb der Menüpunkt-Ebene erforderlich sind: Ein Benutzer sieht entweder alle Dokumente seiner Organisation oder gar keine. Eine Differenzierung nach Dokumententyp — etwa ein Zugriff auf Monitoring-Reports ohne Zugriff auf Vertragsunterlagen — wurde bewusst nicht vorgesehen.
Die Rolle wirkt an zwei Stellen. Erstens steuert sie die Sichtbarkeit des Navigationspunkts: Ohne die Rolle erscheint „Dokumente" nicht im Menü. Zweitens schützt sie die Seite selbst gegen den direkten Aufruf über die URL. Die zweite Prüfung ist die eigentlich sicherheitsrelevante; das Ausblenden des Menüpunkts ist reine Benutzerführung und darf nicht als Schutzmaßnahme betrachtet werden.
\subsection{403 statt 404}
Ein diskutierter Punkt war die Frage, welche Antwort ein Benutzer ohne Berechtigung beim direkten Aufruf von \texttt{/documents} erhalten soll. Die ursprüngliche Fassung der Anforderung sah eine 404-Antwort vor. Die Überlegung dahinter ist verbreitet: Eine 404-Antwort verrät nicht, dass die Ressource überhaupt existiert, und verhindert damit Rückschlüsse auf vorhandene Funktionen.
Im Verlauf der Umsetzung wurde die Anforderung auf eine 403-Antwort geändert. Ausschlaggebend waren zwei Argumente. Zum einen ist die Existenz des Dokumentenbereichs kein Geheimnis — es handelt sich um eine allgemein bekannte Funktion des Kundenportals, nicht um eine verborgene Ressource. Der Informationsgewinn eines Angreifers ist damit gleich null. Zum anderen erschwert eine 404-Antwort die Fehlersuche erheblich: Ein Benutzer, dem versehentlich die Rolle fehlt, erhält dieselbe Antwort wie bei einem Tippfehler in der Adresse. Genau dieser Fall trat später im Abnahmetest tatsächlich ein (siehe Abschnitt~\ref{sec:acceptance-testing}) und bestätigte die Entscheidung.
Der Trade-off lautet also: minimaler, hier praktisch nicht vorhandener Informationsgewinn für einen Angreifer gegen deutlich bessere Diagnostizierbarkeit im Betrieb. Die Entscheidung fiel zugunsten der Diagnostizierbarkeit.
\subsection{Mandantentrennung}
Die zweite Stufe stellt sicher, dass ein berechtigter Benutzer ausschließlich die Dokumente seiner eigenen Organisation sieht. Sie beruht vollständig auf dem Ablagekonzept aus Abschnitt~\ref{sec:s3-layout}: Aus der Organisations-ID im Token wird der zugehörige Kundenordner aufgelöst, und sämtliche Abfragen an den Speicher werden auf dieses Präfix eingeschränkt.
Entscheidend ist dabei, dass die Einschränkung nicht nachträglich auf ein bereits geladenes Ergebnis angewendet wird, sondern bereits Bestandteil der Abfrage ist. Houston lädt zu keinem Zeitpunkt Dokumente fremder Kunden in den Speicher, um sie anschließend herauszufiltern. Ein Fehler in der Darstellungsschicht kann damit nicht zu einer Offenlegung führen.
Abbildung~\ref{fig:auth-sequence} stellt den vollständigen Ablauf dar.
\begin{figure}[H]
\centering
\includegraphics[width=\textwidth]{figures/diagrams/auth-sequence.pdf}
\caption{Ablauf von Authentifizierung und Autorisierung}
\label{fig:auth-sequence}
\end{figure}
\subsection{Absicherung der Einzelzugriffe}
Die Auflistung ist nicht der einzige Zugriffspfad. Auch der Download einzelner Dokumente, der ZIP-Download, die PDF-Vorschau und die Freigabelinks nehmen jeweils einen Dokumentschlüssel entgegen. Für jeden dieser Pfade gilt dieselbe Regel: Der angeforderte Schlüssel wird gegen das aufgelöste Kundenpräfix geprüft, bevor auf den Speicher zugegriffen wird.
Zusätzlich wird der übergebene Pfad daraufhin untersucht, ob er Bestandteile enthält, mit denen sich der Kundenordner verlassen ließe. Ohne diese Prüfung könnte ein Benutzer durch Manipulation des Parameters auf fremde Ordner zugreifen, obwohl das Präfix zunächst korrekt gesetzt war. Diese Anforderung (NFA-6) wurde im Approval-Termin ausdrücklich in die Akzeptanzkriterien aufgenommen.
Die Freigabelinks stellen dabei keine Ausnahme dar: Sie enthalten keine Anmeldeinformationen und gewähren keinen eigenständigen Zugriff. Ein Empfänger, der nicht über die Rolle und die passende Organisationszugehörigkeit verfügt, erhält beim Aufruf dieselbe 403-Antwort wie bei jedem anderen Zugriffsversuch. Der Link dient ausschließlich dazu, innerhalb des berechtigten Personenkreises auf ein bestimmtes Dokument zu verweisen.
+46 -4
View File
@@ -1,7 +1,49 @@
\section{Dokumententypen und Icons}
\label{sec:document-types}
% TODO: 7 Typen hardcoded in Houston (PBI 9560/9549):
% Service-Protokoll, Abnahme Dokumente, SLA-Reports, Monitoring Reports,
% Security Assessments, Abrechnungsdaten, Vertragsunterlagen
% Typzuordnung über Unterordnername, Tabelle im Anhang
\subsection{Der Typenkatalog}
Die sieben Dokumententypen wurden fachlich in der Feature-Beschreibung festgelegt und im Projektverlauf nicht verändert. Sie bilden die Kategorien ab, in denen WorkSimple gegenüber Kunden regelmäßig Unterlagen bereitstellt. Tabelle~\ref{tab:document-types} zeigt den Katalog mit dem jeweiligen Ordnernamen im Speicher.
\begin{table}[H]
\centering
\begin{tabularx}{\textwidth}{@{} l X @{}}
\toprule
\textbf{Ordnername im S3} & \textbf{Fachliche Bedeutung} \\
\midrule
\texttt{Service-Protokoll} & Protokolle erbrachter Serviceleistungen \\
\texttt{Abnahme Dokumente} & Abnahmeprotokolle abgeschlossener Projekte \\
\texttt{SLA-Reports Ticketbearbeitung} & Auswertungen zur Einhaltung vereinbarter Reaktionszeiten \\
\texttt{Monitoring Reports} & Periodische Auswertungen aus der Systemüberwachung \\
\texttt{Security Assessments} & Ergebnisse von Sicherheitsbewertungen \\
\texttt{Abrechnungsdaten} & Abrechnungsunterlagen, gegliedert nach Monat, Jahr und Service \\
\texttt{Vertragsunterlagen} & Verträge, Leistungsscheine und zugehörige Dokumente \\
\bottomrule
\end{tabularx}
\caption{Katalog der Dokumententypen}
\label{tab:document-types}
\end{table}
\subsection{Feste Kodierung statt Konfiguration}
Im Approval-Termin stellte \emph{Timo Walter} die Frage, ob die Kategorien konfigurierbar sein sollen und ob im Betrieb weitere Typen hinzukommen können. Die Entscheidung fiel auf eine feste Kodierung in Houston. Dafür sprechen mehrere Überlegungen.
Der Typenkatalog ist fachlich stabil. Er leitet sich aus den Leistungen ab, die WorkSimple erbringt, und ändert sich allenfalls im Rhythmus von Jahren. Eine Konfigurierbarkeit würde für diesen Änderungsrhythmus eine Verwaltungsoberfläche, ein Speicherformat und eine Migrationsstrategie erfordern — ein Aufwand, der in keinem Verhältnis zum Nutzen steht.
Hinzu kommt, dass die Typen nicht nur als Beschriftung auftreten. Jeder Typ benötigt ein Icon und eine Übersetzung, und beide müssten bei einem frei konfigurierbaren Katalog ebenfalls hinterlegbar sein. Ein neuer Typ ohne Icon fiele in der Oberfläche unangenehm auf. Die feste Kodierung stellt sicher, dass Ordnername, Anzeigetext und Icon stets gemeinsam gepflegt werden.
Der Preis dieser Entscheidung ist, dass das Hinzufügen eines Typs eine Codeänderung und ein Release erfordert. Angesichts der erwarteten Änderungshäufigkeit ist das vertretbar.
\subsection{Verhalten bei unbekannten Ordnern}
Aus der festen Kodierung ergibt sich unmittelbar die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen. Ein Kundenbetreuer könnte in Filestash versehentlich einen Ordner \texttt{Sonstiges} anlegen oder sich beim Namen vertippen.
Das Konzept sieht vor, dass Dokumente in solchen Ordnern nicht verschwinden. Sie werden angezeigt und erhalten ein neutrales Standard-Icon; lediglich die Typzuordnung entfällt. Die Alternative — solche Dokumente auszublenden — wurde verworfen, weil sie ein stilles Fehlverhalten erzeugen würde: Ein hochgeladenes Dokument wäre für den Kunden unsichtbar, ohne dass dies für den Betreuer erkennbar wäre.
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene. Dort führt ein fehlendes Marker-Metadatum zum vollständigen Ausschluss (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung, hier gilt im Zweifel Ausschluss. Innerhalb eines Kundenordners ist die Zugehörigkeit dagegen bereits geklärt, und ein unbekannter Unterordner ist lediglich ein Darstellungsproblem.
\subsection{Übersetzung der Anzeigenamen}
Die Ordnernamen im Speicher dienen zwei verschiedenen Zielgruppen. Für die Kundenbetreuer, die in Filestash arbeiten, müssen sie lesbar und eindeutig sein. Für den Kunden in Houston sollen sie sich in die Oberfläche einfügen.
Aus diesem Grund wird der Ordnername nicht unverändert angezeigt, sondern über eine Ressourcendatei auf einen Anzeigetext abgebildet. Dieses Vorgehen entspricht dem in Houston bereits etablierten Muster zur Lokalisierung und erlaubt es, den Anzeigetext zu ändern, ohne die Struktur im Speicher anzufassen — eine Umbenennung dort würde bedeuten, sämtliche betroffenen Objekte umzukopieren.
+54 -10
View File
@@ -1,13 +1,57 @@
\section{Architekturentscheidung: O(1)-Fast-Path}
\section{Architekturentscheidung: Namensgebung durch Houston}
\label{sec:lookup-decision}
% TODO: PBI 10018 — metadatenbasierter Fast-Path
% ResolveCustomerPrefixAsync: Fast-Path → Kollisions-Fast-Path → O(n)-Fallback + Self-Healing-Rename
% Houston als führende Instanz für Ordnerbenennung
\subsection{Die zugrunde liegende Idee}
% \begin{figure}[H]
% \centering
% \includegraphics[width=\textwidth]{figures/diagrams/lookup-flow.pdf}
% \caption{Ablauf \texttt{ResolveCustomerPrefixAsync}}
% \label{fig:lookup-flow}
% \end{figure}
Alle in Abschnitt~\ref{sec:lookup-research} betrachteten Ansätze versuchen, die Zuordnung zwischen Organisations-ID und Ordnername \emph{zusätzlich} irgendwo zu hinterlegen — in einem Cache, in einem Token, in Efecte oder in einer Zuordnungsdatei. Jeder dieser Ansätze führt damit eine zweite Datenhaltung ein, die mit der ersten konsistent gehalten werden muss.
Die schließlich gewählte Lösung dreht die Fragestellung um. Statt die Zuordnung nachzuschlagen, wird sie \emph{berechenbar} gemacht: Wenn der Ordnername sich deterministisch aus dem Firmennamen ableiten lässt und dieser Firmenname bereits im Anmeldetoken steht, kann Houston den erwarteten Ordnernamen ohne jeden Nachschlagevorgang bestimmen. Aus der Suche wird ein direkter Zugriff.
Voraussetzung dafür ist, dass die Ordner im Speicher tatsächlich so heißen, wie Houston es erwartet. Genau hier setzt die eigentliche Entscheidung an: \textbf{Houston wird zur führenden Instanz für die Namensgebung der Kundenordner.} Weicht ein Ordner vom erwarteten Namen ab, wird nicht der Erwartungswert angepasst, sondern der Ordner umbenannt.
Diese Festlegung ist deshalb vertretbar, weil Houston die Ordnerstruktur ohnehin selbst anlegt (FA-13). Wenn die Anwendung neue Kundenordner erzeugt, ist es folgerichtig, dass sie auch deren Benennung verantwortet. Die Kundenbetreuer verlieren dadurch nichts: Der Ordner heißt weiterhin nach dem Kunden, nur eben in einer normalisierten Form.
\subsection{Ableitung des Ordnernamens}
Der Firmenname aus dem Token kann nicht unverändert als Ordnername verwendet werden. Er kann Zeichen enthalten, die im Schlüssel problematisch sind, führende oder abschließende Leerzeichen aufweisen oder beliebig lang sein. Er wird deshalb durch eine Normalisierungsfunktion geführt, die im weiteren Verlauf als \emph{Slug} bezeichnet wird.
Die Normalisierung folgt zwei Leitgedanken. Sie muss \textbf{deterministisch} sein — derselbe Firmenname muss stets denselben Ordnernamen ergeben, sonst funktioniert der direkte Zugriff nicht. Und sie muss das Ergebnis \textbf{lesbar} halten, weil der Ordner in Filestash von Menschen bedient wird. Aus dem zweiten Punkt folgt eine bewusste Abweichung von der üblichen Praxis: Umlaute werden \emph{nicht} ersetzt. Eine Firma „Müller GmbH" erhält den Ordner \texttt{Müller GmbH} und nicht \texttt{Mueller GmbH}, weil der Betreuer sie andernfalls in der alphabetischen Liste an unerwarteter Stelle suchen müsste. Die Zulässigkeit solcher Zeichen in Objektschlüsseln wurde anhand der Herstellerdokumentation geprüft \autocite{aws-s3-naming, ibm-s3-naming}; die Frage war auch Gegenstand des Code-Reviews.
\subsection{Umgang mit Namenskollisionen}
Firmennamen sind nicht garantiert eindeutig. Efecte lässt zwei Organisationen mit identischem Namen grundsätzlich zu, und sobald der Ordnername aus dem Firmennamen abgeleitet wird, treffen beide auf denselben Zielnamen. Da eine Verwechslung hier unmittelbar bedeuten würde, dass ein Kunde die Dokumente eines anderen sieht, muss dieser Fall abgedeckt sein.
Das Konzept löst ihn nach dem Prinzip „wer zuerst kommt, mahlt zuerst": Die erste Organisation, die den Namen beansprucht, erhält ihn. Jede weitere erhält einen Ordner, dem die Organisations-ID in Klammern angehängt wird, also etwa \texttt{Beispielkunde GmbH (77)}. Dieser Name ist eindeutig, bleibt lesbar und sortiert weiterhin unmittelbar neben dem gleichnamigen Ordner.
Maßgeblich ist dabei eine Sicherheitsregel: Ein Zielname wird nur dann beansprucht, wenn er entweder frei ist oder ausweislich seines Markers bereits der eigenen Organisation gehört. Ein fremder Kundenordner wird unter keinen Umständen überschrieben oder umbenannt. Die Zuordnung entscheidet also immer das Metadatum, niemals der Name — der Name ist nur die Optimierung.
\subsection{Der resultierende Ablauf}
Aus diesen Überlegungen ergibt sich ein dreistufiges Verfahren, das in Abbildung~\ref{fig:lookup-flow} dargestellt ist.
\begin{figure}[H]
\centering
\includegraphics[width=\textwidth]{figures/diagrams/lookup-flow.pdf}
\caption{Auflösung des Kundenordners}
\label{fig:lookup-flow}
\end{figure}
\begin{enumerate}
\item \textbf{Direktzugriff.} Houston bildet den erwarteten Ordnernamen aus dem Firmennamen und ruft die Metadaten des zugehörigen Markers ab. Existiert er und trägt er die richtige Organisations-ID, ist die Auflösung mit einem einzigen Aufruf abgeschlossen. Dies ist der Regelfall.
\item \textbf{Kollisionsprüfung.} Schlägt der erste Schritt fehl — weil der Marker fehlt oder eine fremde Organisations-ID trägt — wird derselbe Zugriff mit dem um die Organisations-ID ergänzten Namen wiederholt. Damit ist der Kollisionsfall mit zwei Aufrufen abgedeckt.
\item \textbf{Rückfallebene.} Erst wenn auch das nicht greift, kommt das ursprüngliche, lineare Verfahren zum Einsatz. Wird dabei ein Ordner mit passender Organisations-ID gefunden, trägt er offenbar einen abweichenden Namen und wird auf den kanonischen Namen umbenannt. Wird kein Ordner gefunden, existiert der Kunde im Speicher noch nicht und die vollständige Struktur wird angelegt.
\end{enumerate}
\subsection{Selbstheilung}
Die entscheidende Eigenschaft dieses Verfahrens liegt im dritten Schritt. Die Rückfallebene beseitigt nicht nur den unmittelbaren Fehlschlag, sondern auch dessen Ursache: Nach der Umbenennung trägt der Ordner den erwarteten Namen, und der nächste Zugriff derselben Organisation wird wieder über den Direktzugriff abgewickelt.
Der teure Pfad wird damit pro Kunde höchstens einmal durchlaufen. Bestehende Ordner, die vor Einführung des Verfahrens angelegt wurden und beliebige Namen tragen, migrieren sich beim ersten Zugriff des jeweiligen Kunden von selbst. Eine gesonderte Migration ist nicht erforderlich.
Ebenso wenig erfordert das Verfahren eine zusätzliche Datenhaltung. Es gibt keinen Cache, der invalidiert werden müsste, kein Feld in einem Fremdsystem und keine Zuordnungsdatei. Der Zustand liegt vollständig im Speicher selbst, und die Anwendung ist zu jedem Zeitpunkt in der Lage, aus einem beliebigen Ausgangszustand den Sollzustand herzustellen.
\subsection{Bewusst offen gelassener Bereich}
Das Verfahren hat einen Preis, der bei der Entscheidung bekannt war: Die Rückfallebene ist nicht mehr nur lesend. Sie benennt Ordner um und legt sie an — und das im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, können zwei solche Anfragen gleichzeitig auf denselben Ordner treffen.
Dieser Umstand wurde während der Umsetzung erkannt, in seinen Auswirkungen analysiert und als eigenes Backlog Item vom laufenden Pull Request abgegrenzt. Die Entscheidung, die Abgrenzung so vorzunehmen, wurde mit dem Team abgestimmt. Abschnitt~\ref{sec:race-conditions} behandelt die konkreten Fehlerszenarien und die erwogenen Gegenmaßnahmen im Detail.
+51 -5
View File
@@ -1,8 +1,54 @@
\section{Problem: Lookup des Kundenordners}
\section{Das Lookup-Problem}
\label{sec:lookup-research}
% TODO: PBI 9857 — S3 kann nicht nach Metadaten-Tags suchen → lineares Scannen aller Top-Level-Prefixes
% Trade-off-Matrix der 6 Varianten (In-Memory Cache, Claim, Efecte-Feld, S3 SelectObject, Search Integration, Prefix im Namen)
% \cite{aws-s3-select}, \cite{storagegrid-search-integration}
\subsection{Entstehung}
% Tabelle~\ref{tab:lookup-tradeoffs} im Anhang
Der Document Explorer war zum Zeitpunkt seines Merges am 31.~Juli 2026 funktionsfähig, enthielt aber eine Schwäche, die bereits im Code-Review angesprochen worden war. Um den Kundenordner zu einer Organisations-ID zu finden, listete Houston sämtliche Ordner auf der obersten Ebene des Buckets auf, rief für jeden davon die Metadaten ab und verglich die hinterlegte \texttt{efecte-org-id} mit der des angemeldeten Benutzers.
Die Ursache liegt in der bereits beschriebenen Eigenschaft von S3: Objekte lassen sich nicht anhand ihrer benutzerdefinierten Metadaten suchen. Die Operation \texttt{ListObjectsV2} liefert Schlüssel, Größe und Änderungszeitpunkt, aber keine benutzerdefinierten Metadaten \autocite{aws-listobjectsv2}. Um an das Marker-Metadatum zu gelangen, ist für jeden Kandidaten ein eigener Aufruf nötig.
Damit wächst der Aufwand für die Auflösung des Kundenordners linear mit der Anzahl der Kunden — und zwar bei \emph{jedem} Seitenaufruf jedes Benutzers. Bei einer zweistelligen Kundenzahl fällt das kaum auf; bei einigen hundert Kunden bedeutet es einige hundert Netzwerkaufrufe, bevor überhaupt das erste Dokument geladen wird. Das Problem ist also nicht akut, aber strukturell: Es verschlechtert sich mit dem Erfolg des Produkts.
Aus dieser Erkenntnis entstand am 28.~Juli ein eigenes Backlog Item. Bemerkenswert ist, dass es bewusst nicht als Umsetzungs-, sondern als \emph{Recherche}-Item angelegt wurde. Das Akzeptanzkriterium lautete nicht „das Problem ist behoben", sondern „es ist sich für einen Lösungsansatz entschieden worden". Diese Zuschnitt-Entscheidung trennt die Frage, welche Lösung die richtige ist, von der Frage, wie sie umzusetzen ist — und macht den Rechercheaufwand als eigenständige Leistung sichtbar, statt ihn in einer Implementierungsaufgabe zu verstecken.
\subsection{Untersuchte Lösungsansätze}
Im Zuge der Recherche wurden sechs Ansätze betrachtet und mit \emph{Timo Walter} sowie den Ansprechpartnern für die Speicherinfrastruktur diskutiert. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen; im Folgenden werden die Ansätze und ihre jeweiligen Schwächen erläutert.
\subsubsection{In-Memory-Cache}
Der naheliegendste Ansatz ist, das Ergebnis der Auflösung im Arbeitsspeicher der Anwendung zwischenzuspeichern. Der erste Aufruf bleibt teuer, alle folgenden sind kostenlos.
Der Ansatz behebt das Problem jedoch nur oberflächlich. Houston läuft in mehr als einer Instanz, sodass jede Instanz ihren eigenen Cache aufbauen müsste. Nach jedem Neustart oder jeder Bereitstellung ist der Cache leer, und es entsteht ein Ansturm teurer Auflösungen. Vor allem aber bleibt der zugrunde liegende lineare Aufwand unverändert bestehen — er wird lediglich seltener bezahlt. Ein Cache ist eine Optimierung, keine Lösung.
\subsubsection{Speicherung im Anmeldetoken}
Alternativ ließe sich der Ordnername bei der Anmeldung ermitteln und als zusätzlicher Claim im Token ablegen. Gegenüber dem Cache verschiebt das den Aufwand von jedem Seitenaufruf auf jede Anmeldung.
Auch hier bleibt der lineare Aufwand erhalten. Hinzu kommt ein Konsistenzproblem: Ein Token ist über seine Laufzeit unveränderlich. Wird der Ordner im Speicher umbenannt, während ein Benutzer angemeldet ist, zeigt der Claim auf einen nicht mehr existierenden Ordner, bis das Token erneuert wird.
\subsubsection{Speicherung als Feld in Efecte}
Der Ordnername könnte auch als zusätzliches Feld an der Organisation in Efecte gepflegt werden. Houston würde ihn dann gemeinsam mit den übrigen Organisationsdaten laden.
Dieser Ansatz löst das Problem technisch, führt aber eine neue Abhängigkeit ein und verlagert die Pflege in ein weiteres System. Die Zuordnung zwischen Kunde und Ordner läge damit an zwei Stellen — im Efecte-Feld und im Marker-Metadatum — die auseinanderlaufen können. Zudem müsste das Feld für jeden Bestandskunden nachgepflegt werden.
\subsubsection{S3 Select}
Die Operation \texttt{SelectObjectContent} erlaubt es, den \emph{Inhalt} eines Objekts serverseitig per SQL-ähnlicher Abfrage zu filtern \autocite{aws-s3-select}. Denkbar wäre gewesen, eine Zuordnungstabelle als Datei im Bucket abzulegen und den passenden Eintrag serverseitig herauszufiltern.
Die Freischaltung wurde beantragt und am 7.~August durch den Betreiber bestätigt (siehe Abschnitt~\ref{sec:infrastructure}). Bei genauerer Betrachtung erwies sich der Ansatz jedoch als unpassend: S3 Select filtert Inhalte einzelner Objekte, nicht Metadaten mehrerer Objekte. Eine Zuordnungsdatei wäre eine zusätzliche, manuell oder programmatisch zu pflegende Struktur — mit demselben Konsistenzproblem wie beim Efecte-Feld, nur ohne dessen Werkzeugunterstützung.
\subsubsection{StorageGRID Search Integration}
Der Search Integration Service spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht damit genau das, was S3 selbst nicht bietet: eine echte Suche über Metadaten \autocite{storagegrid-search-integration}. Fachlich wäre dies die sauberste Lösung gewesen, da sie das Problem an der Wurzel behebt, ohne eine zweite Datenhaltung einzuführen.
Die Anfrage über den Betreiber ergab am 17.~August, dass die Funktion derzeit nicht angeboten wird. Damit schied der Ansatz aus. Er wurde in den Ausblick übernommen (siehe Abschnitt~\ref{sec:outlook}).
\subsubsection{Organisations-ID im Ordnernamen}
Der technisch einfachste Ansatz wäre gewesen, die Organisations-ID zum Bestandteil des Ordnernamens zu machen — etwa als Präfix \texttt{42\_Beispielkunde GmbH}. Der Lookup reduzierte sich dann auf eine einzige Präfixabfrage.
Dieser Ansatz scheiterte an einer fachlichen Anforderung. In der Abstimmung mit \emph{Ralf Schulte} wurde deutlich, dass die Kundenbetreuer die Ordner in Filestash nach Kundennamen sortiert vorfinden müssen, um damit arbeiten zu können. Ein vorangestellter Zahlenschlüssel zerstört diese Sortierung. Der Konflikt ist damit klar benannt: Houston autorisiert über die Organisations-ID, die Betreuer arbeiten über den Kundennamen, und der Speicher bietet keinen Mechanismus, zwischen beiden zu vermitteln.
Die entscheidende Beobachtung war jedoch, dass die Anforderung eine Sortierung nach Kundennamen verlangt — nicht zwingend, dass der Ordnername \emph{ausschließlich} aus dem Kundennamen besteht. Diese Unterscheidung eröffnete den Weg zu der in Abschnitt~\ref{sec:lookup-decision} beschriebenen Lösung.
+55 -10
View File
@@ -1,14 +1,59 @@
\section{Ablagekonzept im S3-Speicher}
\label{sec:s3-layout}
% TODO: Ordnerbaum (Feature 484):
% Bucket → Kundenordner [meta: efecte_org-id] → Typordner/ → Dokumente
% Typ = Unterordner, nicht Metadatum an der Datei
% Leere Ordner werden nicht angezeigt
Das Ablagekonzept bildet die Grundlage für alle weiteren Entwurfsentscheidungen. Es muss zwei Aufgaben gleichzeitig erfüllen: Es muss festlegen, welche Dokumente zu welchem Kunden gehören, und es muss den fachlichen Typ eines Dokuments abbilden. Beides geschieht ausschließlich über die Struktur im Speicher, ohne eine zusätzliche Datenbank.
% \begin{figure}[H]
% \centering
% \includegraphics[width=0.75\textwidth]{figures/diagrams/s3-layout.pdf}
% \caption{S3-Ablagestruktur}
% \label{fig:s3-layout}
% \end{figure}
\subsection{Präfixe statt Ordner}
S3 kennt technisch keine Ordner. Jedes Objekt wird über einen Schlüssel adressiert, der als Zeichenkette in einem flachen Namensraum liegt. Die scheinbare Hierarchie entsteht erst beim Auflisten: Übergibt man der Operation \texttt{ListObjectsV2} ein Präfix und ein Trennzeichen, liefert der Dienst nur die Objekte unterhalb dieses Präfixes zurück sowie die gemeinsamen Teilpräfixe der nächsten Ebene \autocite{aws-listobjectsv2}. Ein Schlüssel wie
\begin{quote}
\texttt{Beispielkunde GmbH/Service-Protokoll/Protokoll-2026-07.pdf}
\end{quote}
wird in einem Dateibrowser wie Filestash als zweistufige Ordnerhierarchie dargestellt, ist im Speicher jedoch nur eine einzelne Zeichenkette. Diese Eigenschaft ist für das Konzept vorteilhaft, weil sie das Auflisten eines Kundenordners auf eine einzige Präfixabfrage reduziert. Sie hat aber auch eine Konsequenz, die im weiteren Verlauf noch bedeutsam wird: Ein „Ordner" existiert erst dann, wenn mindestens ein Objekt mit dem entsprechenden Präfix vorhanden ist. Ein leerer Ordner lässt sich nur simulieren, indem ein Platzhalterobjekt angelegt wird, dessen Schlüssel auf das Trennzeichen endet.
Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\textwidth]{figures/diagrams/s3-layout.pdf}
\caption{Ablagestruktur im S3-Speicher}
\label{fig:s3-layout}
\end{figure}
\subsection{Kundenzuordnung über ein Marker-Objekt}
Auf der obersten Ebene des Buckets liegt für jeden Kunden genau ein Ordner. Um diesen Ordner einer Organisation zuzuordnen, wird an dem Platzhalterobjekt, das den Ordner repräsentiert, ein benutzerdefiniertes Metadatum \texttt{efecte-org-id} hinterlegt. Dieses Objekt wird im Folgenden als \emph{Marker} bezeichnet.
Der Marker erfüllt zwei Zwecke gleichzeitig. Erstens sorgt er dafür, dass der Kundenordner auch dann existiert, wenn noch kein einziges Dokument abgelegt wurde — andernfalls wäre ein frisch angelegter Kunde im Speicher unsichtbar. Zweitens trägt er die Information, welcher Organisation der Ordner gehört. Damit ist die Zuordnung an genau einer Stelle hinterlegt und muss nicht an jedem einzelnen Dokument wiederholt werden.
Die Entscheidung, die Organisations-ID als Metadatum und nicht als Bestandteil des Ordnernamens zu führen, wurde in der Feature-Analyse getroffen (siehe Abschnitt~\ref{sec:feature-analysis}). Sie ist fachlich motiviert: Die Ordner werden von den Kundenbetreuern über Filestash gepflegt, und ein Ordnername wie \texttt{42} oder \texttt{Beispielkunde GmbH (42)} wäre dort schwer zu handhaben. Der lesbare Firmenname bleibt daher der Ordnername, die technische Zuordnung wandert in die Metadaten.
Genau diese Entscheidung erzeugt allerdings das zentrale technische Problem des Projekts, das in Abschnitt~\ref{sec:lookup-research} behandelt wird: Houston kennt aus dem Authentifizierungstoken die Organisations-ID, muss daraus aber den Ordnernamen ermitteln — und S3 bietet keine Möglichkeit, Objekte nach Metadaten zu durchsuchen.
\subsection{Typisierung über Unterordner}
Innerhalb des Kundenordners liegt für jeden der sieben fachlichen Dokumententypen ein Unterordner. Der Typ eines Dokuments ergibt sich daraus, in welchem Unterordner es abgelegt ist. Dokumente, die direkt im Kundenordner liegen, gelten als untypisiert.
Auch diese Festlegung stammt aus der Feature-Analyse. Die Alternative wäre gewesen, den Typ als Metadatum an jeder einzelnen Datei zu hinterlegen. Der Vergleich beider Ansätze fällt eindeutig aus:
\begin{itemize}
\item \textbf{Pflegeaufwand:} Ein Metadatum müsste bei jedem Upload manuell gesetzt werden. Filestash bietet hierfür keine komfortable Unterstützung. Das Ablegen in einem Ordner ist dagegen die natürliche Bedienhandlung.
\item \textbf{Abfragbarkeit:} Der Typ ist als Präfixbestandteil unmittelbar aus dem Schlüssel ablesbar. Beim Auflisten fällt er ohne Zusatzkosten mit an. Ein Metadatum müsste dagegen für jedes Objekt einzeln abgerufen werden, da \texttt{ListObjectsV2} benutzerdefinierte Metadaten nicht mitliefert. Bei $n$ Dokumenten wären das $n$ zusätzliche Aufrufe.
\item \textbf{Filterung:} Eine Filterung nach Typ reduziert sich auf eine Präfixabfrage und ist damit serverseitig umsetzbar.
\end{itemize}
Dem stehen zwei Nachteile gegenüber. Zum einen kann ein Dokument nur genau einen Typ haben, da es nur an einer Stelle liegen kann; eine Mehrfachzuordnung ist ausgeschlossen. Zum anderen erfordert die Typisierung, dass die Ordner überhaupt existieren — was die automatische Anlage der Ordnerstruktur zu einer eigenen Anforderung macht (FA-13). Beide Einschränkungen wurden bewusst in Kauf genommen.
\subsection{Sichtbarkeitsregeln}
Für die Darstellung gelten drei Regeln, die sich aus der Feature-Beschreibung ergeben:
\begin{enumerate}
\item \textbf{Ordner werden nicht als Einträge angezeigt.} Die Liste zeigt ausschließlich Dokumente; die Ordnerstruktur wird über Typ-Icons und Filter abgebildet, nicht über eine navigierbare Hierarchie. Der Benutzer sieht also eine flache Liste aller seiner Dokumente.
\item \textbf{Leere Typordner erscheinen nicht.} Ein Kunde, für den noch keine Monitoring-Reports abgelegt wurden, sieht diesen Typ nicht als leere Kategorie.
\item \textbf{Ordner ohne gültiges Marker-Metadatum werden ignoriert.} Legt jemand versehentlich einen Ordner auf oberster Ebene an, ohne eine Organisations-ID zu hinterlegen, bleibt dieser für alle Kunden unsichtbar. Der Fall wird protokolliert, führt aber nicht zu einem Fehler und niemals zu einem Zugriff.
\end{enumerate}
Die dritte Regel ist eine Sicherheitsmaßnahme: Sie stellt sicher, dass ein Ordner nur dann für einen Kunden sichtbar wird, wenn seine Zugehörigkeit ausdrücklich hinterlegt ist. Ein fehlendes Metadatum führt zum Ausschluss, nicht zu einer Vermutung.
+35 -3
View File
@@ -1,5 +1,37 @@
\section{UI-Konzept und Clickdummy}
\section{UI-Konzept}
\label{sec:ui-concept}
% TODO: Hanna Ebner, Branch Clickdummy_Dokumente (2026-07-22)
% Listenansicht, Typfilter oben als Buttons, flexible Tabellenspalten
\subsection{Zuarbeit über einen Clickdummy}
Das Oberflächenkonzept wurde nicht als Mockup in einem Entwurfswerkzeug erstellt, sondern von \emph{Hanna Ebner} als lauffähiger Clickdummy in einem eigenen Branch des Houston-Repositories umgesetzt und am 22.~Juli 2026 am Feature verlinkt.
Diese Form der Zuarbeit hat gegenüber einem Bildentwurf spürbare Vorteile. Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch das Ergebnis von vornherein zum übrigen Portal passt. Fragen zu Abständen, Schriftgrößen oder Farben stellen sich gar nicht erst, weil sie durch das vorhandene Stylesheet beantwortet werden. Zudem ist das Ergebnis unmittelbar bedienbar: Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren, statt sie aus einer statischen Abbildung erschließen zu müssen. Für die Umsetzung bedeutete das, dass der Clickdummy als Referenz für das Markup dienen konnte und in mehreren Product Backlog Items ausdrücklich als solche benannt wurde.
\subsection{Flache Liste statt navigierbarer Hierarchie}
Die auffälligste Entwurfsentscheidung ist, dass der Dokumentenbereich trotz seiner Bezeichnung als „Document Explorer" keine navigierbare Ordnerhierarchie darstellt. Der Benutzer sieht eine flache Liste aller seiner Dokumente; die Typzugehörigkeit wird über ein Icon und über Filter ausgedrückt, nicht über ein Hineinnavigieren in Ordner.
Der Grund liegt im erwarteten Nutzungsverhalten. Ein Kunde sucht in aller Regel ein bestimmtes Dokument — den letzten Monitoring-Report oder einen konkreten Vertrag. Bei einer Hierarchie müsste er zunächst wissen, in welcher Kategorie es abgelegt ist, und sich dorthin durchklicken. Die flache Liste erlaubt es dagegen, unmittelbar zu suchen oder zu filtern. Da die Hierarchie ohnehin nur zwei Ebenen tief ist und die zweite Ebene aus sieben festen Kategorien besteht, wäre der Navigationsaufwand in keinem Verhältnis zum Nutzen gestanden.
Diese Entscheidung schlug sich auch in der Formulierung der Anforderungen nieder: Die ursprüngliche Beschreibung sprach von Dokumenten als Kacheln, wurde im Verlauf jedoch auf eine Zeilendarstellung in einer Liste geändert. Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen und lässt sich später um weitere Spalten erweitern.
\subsection{Aufbau der Seite}
Die Seite gliedert sich von oben nach unten in vier Bereiche:
\begin{enumerate}
\item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird.
\item Darunter eine Reihe von \textbf{Filterelementen}, je eines pro Dokumententyp, mit denen sich Typen ein- und ausblenden lassen.
\item Die \textbf{Dokumentenliste} als Tabelle. Jede Zeile enthält das Typ-Icon, den Dokumentnamen sowie die Aktionen Herunterladen, Teilen und — bei PDF-Dateien — Vorschau. Eine Auswahlbox am Zeilenanfang dient der Mehrfachauswahl für den ZIP-Download.
\item Am unteren Rand die \textbf{Blätterelemente} zum Wechsel zwischen den Seiten sowie die Auswahl der Seitengröße.
\end{enumerate}
Die Tabellenstruktur wurde bewusst erweiterbar angelegt. In der Feature-Beschreibung ist ausdrücklich festgehalten, dass weitere Spalten — etwa für eine Vorschau oder zusätzliche Auswahlmöglichkeiten — ergänzt werden können, ohne den Aufbau zu verändern.
\subsection{Konsistenz zur bestehenden Anwendung}
Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen Houston-Seiten bedienen lassen soll (NFA-4). Das betrifft insbesondere die Paginierung, die wie an anderer Stelle eine benutzerseitig wählbare Seitengröße anbietet, und die Suche, die dem gewohnten Verhalten folgen soll.
Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die zunächst umgesetzte Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren des Feldes ein — eine für sich genommen sinnvolle Funktion, die es auf den übrigen Seiten jedoch nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung sich nicht vollständig aus einer Beschreibung ableiten lässt, sondern letztlich am Vergleich mit dem Bestand geprüft werden muss.
Ein zweiter Punkt betrifft das Zusammenspiel von Freigabelinks und Paginierung. Ein Link, der auf ein bestimmtes Dokument verweist, muss auch dann funktionieren, wenn dieses Dokument nicht auf der ersten Seite liegt. Das Konzept sieht deshalb vor, dass der Freigabelink nicht nur das Dokument benennt, sondern beim Weiterleiten auch die passenden Abfrageparameter setzt, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird.
+2 -5
View File
@@ -1,15 +1,12 @@
\section{Ausgangssituation}
\label{sec:initial-situation}
\section{Ausgangssituation}
\label{sec:initial-situation}
Die Idee, Kunden über Houston Zugang zu ihren Dokumenten zu ermöglichen, besteht seit November 2023: \emph{Nicole Kimmel} legte damals das Feature~484 „Dokumente" mit zwei Stichpunkten an — „Vertrag, Betriebshandbuch, Feinkonzepte an zentraler Stelle abgelegt" und „Rechnungen einsehbar". Diese Notiz blieb über zweieinhalb Jahre nahezu unverändert im Backlog und spiegelt die damaligen Bedürfnisse wider, ohne einen konkreten Lösungsansatz zu beschreiben.
Zum Zeitpunkt des Projektbeginns im Sommer 2026 gab es keinen strukturierten Prozess, über den Kunden selbstständig auf ihre Dokumente zugreifen konnten. Verträge, Berichte und ähnliche Unterlagen wurden punktuell per E‑Mail oder über Dateiablagen bereitgestellt. Daraus ergaben sich mehrere Probleme:
Zum Zeitpunkt des Projektbeginns im Sommer 2026 gab es keinen strukturierten Prozess, über den Kunden selbstständig auf ihre Dokumente zugreifen konnten. Verträge, Berichte und ähnliche Unterlagen wurden punktuell per E-Mail oder über Dateiablagen bereitgestellt. Daraus ergaben sich mehrere Probleme:
\begin{itemize}
\item \textbf{Fehlende Zentralisierung:} Dokumente lagen verteilt in E‑Mails, Dateiablagen und lokalen Verzeichnissen ohne einheitlichen Zugangspunkt.
\item \textbf{Fehlende Zentralisierung:} Dokumente lagen verteilt in E-Mails, Dateiablagen und lokalen Verzeichnissen ohne einheitlichen Zugangspunkt.
\item \textbf{Kein Self-Service für Kunden:} Kunden mussten Dokumente aktiv anfordern, anstatt sie eigenständig abrufen zu können.
\item \textbf{Kein strukturierter Überblick:} Eine Übersicht nach Dokumententypen oder zeitlichem Verlauf war nicht vorhanden.
\item \textbf{Kein sicherer, mandantengetrennter Zugriff:} Es existierte kein technischer Mechanismus, der sicherstellte, dass ein Kunde ausschließlich seine eigenen Dokumente einsehen konnte.
-3
View File
@@ -1,6 +1,3 @@
\section{Beteiligte und Rollen}
\label{sec:participants}
\section{Projektbeteiligte}
\label{sec:participants}
+1 -4
View File
@@ -1,12 +1,9 @@
\section{Projektbeschreibung}
\label{sec:project-description}
\section{Projektbeschreibung}
\label{sec:project-description}
Das Projekt \emph{Houston Dokumente} hat zum Ziel, Kunden im Kundenportal Houston einen zentralen Bereich bereitzustellen, in dem sie ihre Dokumente einsehen und herunterladen können. Die Dokumente werden in einem S3-Speichersystem abgelegt und gepflegt; den Kunden werden sie über eine neue Houston-Seite zugänglich gemacht.
Bisher existierte kein einheitlicher, zentraler Zugangspunkt für kundenbezogene Dokumente wie Verträge, Berichte oder Protokolle. Diese wurden punktuell per E‑Mail oder Dateiablage bereitgestellt und waren für Kunden nicht selbstständig abrufbar. Die neue Dokumentenseite in Houston löst diese Situation ab: Mitarbeiter pflegen die Dokumente über ein internes Dateiverwaltungswerkzeug direkt im S3-Speicher, Kunden können sie anschließend strukturiert abrufen.
Bisher existierte kein einheitlicher, zentraler Zugangspunkt für kundenbezogene Dokumente wie Verträge, Berichte oder Protokolle. Diese wurden punktuell per E-Mail oder Dateiablage bereitgestellt und waren für Kunden nicht selbstständig abrufbar. Die neue Dokumentenseite in Houston löst diese Situation ab: Mitarbeiter pflegen die Dokumente über ein internes Dateiverwaltungswerkzeug direkt im S3-Speicher, Kunden können sie anschließend strukturiert abrufen.
Der Dokumentenbereich unterscheidet sieben fachlich definierte Dokumententypen — darunter Service-Protokolle, SLA-Reports, Monitoring Reports und Vertragsunterlagen — und gliedert die Anzeige anhand dieser Typen. Darüber hinaus umfasst das Projekt eine Suchfunktion, Filter nach Dokumententyp, Paginierung, einen PDF-Viewer, den Download einzelner Dateien sowie das Herunterladen mehrerer Dokumente als ZIP-Archiv. Zusätzlich werden sogenannte URL-Dateien unterstützt, mit denen beliebige Webadressen — etwa Links zu Teams-Kanälen oder SharePoint-Seiten — in der Dokumentenliste verknüpft werden können.
-3
View File
@@ -1,9 +1,6 @@
\section{Projektabgrenzung}
\label{sec:project-scope}
\section{Projektabgrenzung}
\label{sec:project-scope}
Folgende Punkte sind explizit \emph{nicht} Bestandteil des Projekts:
\begin{itemize}
-3
View File
@@ -1,9 +1,6 @@
\section{Systemlandschaft}
\label{sec:system-landscape}
\section{Systemlandschaft}
\label{sec:system-landscape}
Der Dokumentenbereich ist in die bestehende Systemlandschaft von WorkSimple eingebettet. Abbildung~\ref{fig:system-context} zeigt den Systemkontext und das Zusammenspiel der beteiligten Systeme.
\begin{figure}[H]
-3
View File
@@ -1,9 +1,6 @@
\section{Zielsituation}
\label{sec:target-situation}
\section{Zielsituation}
\label{sec:target-situation}
Mit der Fertigstellung des Dokumentenbereichs erhalten Kunden im Kundenportal Houston erstmals einen strukturierten, selbstständig nutzbaren Zugang zu ihren Dokumenten. Die Zielsituation zeichnet sich durch eine zentrale Ablage im S3-Speicher und eine mandantengetrennte Darstellung in Houston aus.
Der Dokumentenbereich bietet folgende zentrale Funktionen:
+24 -11
View File
@@ -1,14 +1,27 @@
\section{Backlog-Schnitt und Schätzung}
\section{Backlog und Entwicklungsprozess}
\label{sec:backlog}
% TODO: 14 PBIs + 1 Bug unter Feature 484
% Prozess: DoR, DoD, Approval-Team, Sprints 15–17.2026
% Siehe Tabelle~\ref{tab:backlog} im Anhang
\subsection{Entwicklungsprozess}
% Gantt:
% \begin{figure}[H]
% \centering
% \includegraphics[width=\textwidth]{figures/diagrams/gantt-plan.pdf}
% \caption{Zeitplanung über Sprints 15–17.2026}
% \label{fig:gantt}
% \end{figure}
Die Softwareentwicklung bei der Unicorn Development erfolgt nach einem agilen Prozess mit zweiwöchigen Sprints, verwaltet in Azure DevOps. Jedes Work Item durchläuft dabei einen definierten Zustandsautomaten, der in Abbildung~\ref{fig:ado-workflow} dargestellt ist.
\begin{figure}[H]
\centering
\includegraphics[width=0.55\textwidth]{figures/diagrams/ado-workflow.pdf}
\caption{Zustände eines Work Items in Azure DevOps}
\label{fig:ado-workflow}
\end{figure}
Ein neu angelegtes Item befindet sich zunächst im Zustand \texttt{New}. Sobald Beschreibung und Akzeptanzkriterien vollständig sind, wird es zur Freigabe eingereicht (\texttt{To Approve}). Im Approval-Termin prüft das Team, ob das Item der \emph{Definition of Ready} genügt, stellt Rückfragen und schätzt den Aufwand in Story Points. Erst danach gilt es als \texttt{Approved} und kann in einen Sprint gezogen werden (\texttt{Committed}). Nach erfolgreichem Merge des zugehörigen Pull Requests wechselt das Item nach \texttt{Dev Completed}; die fachliche Abnahme überführt es schließlich nach \texttt{Test Completed}.
Dieser Prozess erwies sich im Projektverlauf als wirksam: Die Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Frage, wann und wie oft die Ordnerstruktur geprüft wird, oder ob Namensbeschränkungen für Kundenordner zu berücksichtigen sind.
\subsection{Schnitt der Product Backlog Items}
Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 — unmittelbar nach der Feature-Analyse — wurden die acht Items der Kernfunktionalität angelegt. Sie orientieren sich direkt an der Gliederung der Feature-Beschreibung: Der Document Explorer bildet die Basis, die im Abschnitt \emph{Feature-Creep} genannten Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) wurden jeweils zu eigenen Items. Am 7.~Juli 2026 kam das Item zur Typisierung über Icons hinzu, am 8.~Juli das Item zur automatischen Ordneranlage.
Eine zweite Gruppe von Items entstand erst während der Umsetzung. Am 22.~Juli ergänzte die UI-Zuarbeit die Anforderungen um Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Item zum Kundenordner-Lookup mündete. Am 17.~August kam schließlich das Item zu den Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}).
Insgesamt hängen 14 Product Backlog Items und ein Bug am Feature. Die vollständige Übersicht mit Aufwandsschätzung, Sprint und Status findet sich in Tabelle~\ref{tab:backlog} im Anhang. Die geschätzten Aufwände summieren sich auf 46 Story Points, verteilt auf 35 Punkte in Sprint~15.2026 und 11 Punkte in Sprint~16.2026.
Bemerkenswert ist die Aufteilung: Die im Voraus geschnittenen Items betreffen ausschließlich fachliche Funktionen. Sämtliche nachgeschobenen Items der zweiten Gruppe entstanden aus technischen Problemen, die erst bei der Implementierung sichtbar wurden — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
+12 -4
View File
@@ -1,7 +1,15 @@
\section{Feature-Analyse}
\label{sec:feature-analysis}
% TODO: 18.06.2026 — 4 Rückfragen von Linus an Thomas, Antworten im selben Ticket (Feature 484 Rev 17–32)
% - Autorisierung via Metadatum: ja
% - Filestash als externe Website (kein Custom-UI): ja
% - ...
Das Feature~484 „Dokumente" existierte seit November 2023 als zweizeilige Bedarfsnotiz im Backlog. Am 10.~Juni 2026 arbeitete \emph{Thomas Drewermann} es in mehreren aufeinanderfolgenden Bearbeitungen zu einer vollständigen Feature-Beschreibung aus. Dabei entstanden die Entscheidung für einen S3-Speicher als Ablage, die Festlegung auf Filestash als internes Verwaltungswerkzeug, der Katalog der sieben Dokumententypen sowie die Abschnitte \emph{Feature-Creep} und \emph{Out of Scope}.
Am 18.~Juni 2026 führte ich eine Feature-Analyse durch, um die verbliebenen Unklarheiten vor dem Backlog-Schnitt zu beseitigen. Vier Rückfragen wurden als Kommentare am Work Item gestellt und noch am selben Tag beantwortet. Drei davon entschieden Architekturfragen:
\begin{enumerate}
\item \textbf{Autorisierung über Metadaten:} Auf die Frage, ob die Efecte-Organisations-ID als Metadatum am Kundenordner gespeichert und die Berechtigung darüber aufgelöst werden könne, lautete die Antwort \emph{ja}. Damit war das Autorisierungskonzept festgelegt (siehe Abschnitt~\ref{sec:authorization}).
\item \textbf{Filestash als externes Werkzeug:} Es wurde bestätigt, dass Filestash unverändert als Verwaltungsoberfläche genutzt wird und \emph{nicht} als Referenz für einen Nachbau in Houston dient. Damit beschränkte sich der Implementierungsaufwand auf die Kundensicht.
\item \textbf{Typisierung über Ordner statt Metadaten:} Auf die Frage, ob der Dokumententyp als Metafeld an der Datei vermerkt werden solle, lautete die Antwort \emph{nein, Ordner}. Der Typ wird also über den Unterordner bestimmt, in dem das Dokument liegt.
\item \textbf{Bedeutung der URL-Dateien:} Die vierte Frage klärte, dass mit der Verlinkung zu Teams oder SharePoint gemeint ist, \emph{externe} Ressourcen in den Dokumentenbereich einzubetten — und nicht umgekehrt Houston-Dokumente nach außen zu teilen. Daraus entstand das Product Backlog Item zur Unterstützung von \texttt{.url}-Dateien.
\end{enumerate}
Die dritte Entscheidung erwies sich im weiteren Verlauf als die folgenreichste. Die Abbildung des Typs über die Ordnerstruktur macht die Filterung nach Typ günstig, da sie sich auf ein Präfix-Listing reduziert. Sie erzwingt jedoch, dass die Ordnerstruktur für jeden Kunden vorhanden ist, und macht damit die automatische Anlage der Ordner zu einer eigenen Anforderung. Zugleich erschwert sie die typübergreifende Suche, da hierfür mehrere Präfixe durchlaufen werden müssen.
+31 -12
View File
@@ -1,15 +1,34 @@
\section{Infrastrukturbeschaffung}
\section{Beschaffung der S3-Infrastruktur}
\label{sec:infrastructure}
% TODO: Service Requests an Maschinenraum (2026-07-22), Provisionierung durch Advanced Unibyte,
% 3 Buckets (DEV/TEST/PROD), Credentials in Passbolt (2026-07-27)
% Anfrage S3 Select (2026-07-30), Aktivierung durch AU (2026-08-07)
% Search Integration: von AU nicht bereitgestellt (2026-08-17)
Der für das Projekt benötigte S3-Speicher stand zu Projektbeginn nicht zur Verfügung und musste über den internen Service-Desk-Prozess beantragt werden. Da der Speicher nicht von WorkSimple selbst, sondern von Advanced Unibyte betrieben wird, war die Beschaffung mit einem mehrstufigen Abstimmungsweg verbunden. Abbildung~\ref{fig:timeline-infra} zeigt den zeitlichen Verlauf.
% Timeline-Diagramm:
% \begin{figure}[H]
% \centering
% \includegraphics[width=\textwidth]{figures/diagrams/timeline-infra.pdf}
% \caption{Chronologie der Infrastrukturbeschaffung}
% \label{fig:timeline-infra}
% \end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\textwidth]{figures/diagrams/timeline-infra.pdf}
\caption{Chronologie der Infrastrukturbeschaffung}
\label{fig:timeline-infra}
\end{figure}
\subsection{Bereitstellung der Buckets}
Am 22.~Juli 2026 stellte ich den Service Request „Houston DEV S3 Documents Speicher" an das interne Infrastruktur-Team. Der Request wurde am 23.~Juli \emph{Alexander Wagner} zugewiesen und am 27.~Juli abgeschlossen: Advanced Unibyte hatte die Buckets angelegt, die Zugangsdaten wurden im Passwortmanager Passbolt hinterlegt. Insgesamt wurden drei Buckets bereitgestellt — je einer für die Entwicklungs-, Test- und Produktivumgebung.
Vom Antrag bis zur Verfügbarkeit vergingen fünf Arbeitstage. Da der Document Explorer als erstes Item ohnehin erst am 27.~Juli in die Umsetzung ging, entstand hieraus keine Verzögerung.
\subsection{Freischaltung zusätzlicher Funktionen}
Deutlich aufwendiger gestaltete sich die Klärung, welche S3-Funktionen die StorageGRID-Installation tatsächlich unterstützt. Im Rahmen der Recherche zur Optimierung des Kundenordner-Lookups (siehe Abschnitt~\ref{sec:lookup-research}) kamen zwei Funktionen als mögliche Lösungen in Betracht:
\begin{itemize}
\item \textbf{S3 Select} (\texttt{SelectObjectContent}) erlaubt es, Inhalte einzelner Objekte serverseitig per SQL-ähnlicher Abfrage zu filtern \autocite{aws-s3-select, storagegrid-s3-select}.
\item Der \textbf{Search Integration Service} von StorageGRID spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht dadurch eine echte Suche über Metadaten \autocite{storagegrid-search-integration}.
\end{itemize}
Am 30.~Juli beantragte ich die Freischaltung beider Funktionen. Da hierfür der Betreiber einbezogen werden musste, kontaktierte \emph{Lennart Meinert} am 4.~August Advanced Unibyte. Am 6.~August benannte er die drei betroffenen Buckets, am 7.~August bestätigte Advanced Unibyte die Aktivierung von S3 Select für alle drei Umgebungen.
Für den Search Integration Service fiel die Antwort anders aus: Am 17.~August teilte Advanced Unibyte mit, dass diese Funktion derzeit nicht angeboten werde; das Thema wurde intern an den dortigen Product Owner eskaliert. Am 21.~August schlug Advanced Unibyte ein Folgegespräch vor. Da zu diesem Zeitpunkt bereits eine Lösung ohne serverseitige Suche gefunden und umgesetzt war (siehe Abschnitt~\ref{sec:lookup-decision}), wurde der Service Request geschlossen und das Thema in den Ausblick verschoben.
\subsection{Bewertung}
Zwischen dem ersten Antrag und der abschließenden Klärung lagen vier Wochen. Diese Vorlaufzeit war zu Projektbeginn nicht eingeplant und beeinflusste die Architekturentscheidung unmittelbar: Ein Lösungsansatz, der auf einer erst noch zu beschaffenden Fremdleistung beruht, ist innerhalb eines Projektzeitraums von wenigen Wochen nicht belastbar. Die schließlich gewählte Lösung kommt daher ohne Erweiterung der Speicherfunktionen aus.
+15 -2
View File
@@ -1,5 +1,18 @@
\section{Einarbeitung}
\label{sec:onboarding}
% TODO: S3-API, AWS SDK für .NET, StorageGRID-Dokumentation
% Quellen: note-1785344962361 (S3-Recherche mit Timo), note-1785413874041
Vor Beginn der Implementierung war eine Einarbeitung in die für das Projekt relevanten Technologien erforderlich. Im Zentrum stand dabei der S3-Objektspeicher, mit dem im bisherigen Verlauf des Praktikums noch nicht gearbeitet worden war.
\subsection{S3 als Objektspeicher}
S3 (\emph{Simple Storage Service}) ist kein klassisches Dateisystem, sondern ein Objektspeicher. Objekte werden über einen flachen Schlüsselraum adressiert; eine Ordnerhierarchie existiert technisch nicht. Was in Werkzeugen wie Filestash als Ordner dargestellt wird, ist lediglich ein Präfix im Objektschlüssel, das in Verbindung mit einem Trennzeichen (\texttt{Delimiter}) beim Auflisten hierarchisch interpretiert wird \autocite{aws-listobjectsv2}. Diese Eigenschaft prägt das gesamte Ablagekonzept (siehe Abschnitt~\ref{sec:s3-layout}) und war für mehrere spätere Architekturentscheidungen ausschlaggebend.
Eine zweite wesentliche Erkenntnis betrifft die Abfragemöglichkeiten: An Objekten können zwar benutzerdefinierte Metadaten hinterlegt werden, S3 bietet jedoch \emph{keine} Möglichkeit, Objekte anhand dieser Metadaten zu suchen. Ein Lookup nach einem Metadatenwert erfordert daher das Auflisten aller in Frage kommenden Objekte und eine anschließende clientseitige Filterung. Dieses Detail wurde erst im Verlauf der Implementierung zum zentralen technischen Problem des Projekts (siehe Abschnitt~\ref{sec:lookup-research}).
\subsection{AWS SDK für .NET}
Der Zugriff auf den S3-Speicher erfolgt aus Houston heraus über das AWS SDK für .NET. Zentrale Schnittstelle ist \texttt{IAmazonS3}, über die sämtliche Operationen (\texttt{ListObjectsV2}, \texttt{GetObject}, \texttt{PutObject}, \texttt{CopyObject}, \texttt{DeleteObjects}) ausgeführt werden. Da \texttt{IAmazonS3} eine Schnittstelle ist, lässt sie sich in Unit-Tests durch ein Mock ersetzen, was für die spätere Testabdeckung des Kundenordner-Lookups entscheidend war (siehe Abschnitt~\ref{sec:unit-tests}).
\subsection{NetApp StorageGRID}
Der eingesetzte Speicher wird nicht bei Amazon betrieben, sondern von Advanced Unibyte auf Basis von NetApp StorageGRID bereitgestellt. StorageGRID ist S3-kompatibel, implementiert die S3-API jedoch nicht vollständig. Welche Funktionen tatsächlich verfügbar sind, hängt von der Konfiguration der Installation ab und musste im Einzelfall geklärt werden. Diese Einschränkung betraf im Projektverlauf sowohl \texttt{SelectObjectContent} \autocite{storagegrid-s3-select} als auch den \emph{Search Integration Service} \autocite{storagegrid-search-integration} und führte zu den in Abschnitt~\ref{sec:infrastructure} beschriebenen Abstimmungen.
+31 -3
View File
@@ -1,5 +1,33 @@
\section{Anforderungserhebung}
\section{Anforderungen}
\label{sec:requirements}
% TODO: Funktionale und nichtfunktionale Anforderungen aus den PBI-ACs
% Tabelle: Anforderungsmatrix (funktional / nichtfunktional → PBI)
Aus der Feature-Beschreibung und der Analyse ergaben sich die folgenden Anforderungen. Die funktionalen Anforderungen wurden anschließend in Product Backlog Items überführt; die vollständige Zuordnung findet sich in Tabelle~\ref{tab:requirements} im Anhang.
\subsection{Funktionale Anforderungen}
\begin{itemize}
\item \textbf{FA-1 — Dokumentenliste:} Auf der Seite \texttt{/documents} werden alle Dokumente der Organisation des angemeldeten Benutzers als Liste dargestellt. Ordner selbst werden nicht als Einträge angezeigt; leere Typordner erscheinen nicht.
\item \textbf{FA-2 — Mandantentrennung:} Ein Benutzer sieht ausschließlich Dokumente, die im Ordner seiner Organisation liegen. Die Zuordnung erfolgt über die \texttt{efecte-org-id} am Kundenordner.
\item \textbf{FA-3 — Rollenbasierter Zugriff:} Der Navigationspunkt ist nur bei vorhandener Rollenberechtigung sichtbar. Ein direkter Aufruf ohne Berechtigung führt zu einer 403-Antwort.
\item \textbf{FA-4 — Typisierung und Icons:} Jedes Dokument wird mit einem Icon dargestellt, das aus dem Unterordner abgeleitet wird. Unbekannte Typen erhalten ein Standard-Icon.
\item \textbf{FA-5 — Suche:} Dokumente können serverseitig nach ihrem Titel durchsucht werden, inklusive Teiltreffern. Ein leeres Suchfeld zeigt wieder die vollständige Liste.
\item \textbf{FA-6 — Typfilter:} Unterhalb der Suchleiste kann je Dokumententyp ein Element zum Ein- und Ausblenden ausgewählt werden.
\item \textbf{FA-7 — Paginierung:} Die Liste wird seitenweise dargestellt; die Seitengröße ist durch den Benutzer festlegbar, analog zu den übrigen Houston-Seiten.
\item \textbf{FA-8 — Einzeldownload:} Jeder Eintrag besitzt einen Download-Button. Der Download erfolgt über eine zeitlich begrenzte Pre-Signed URL \autocite{aws-presigned-urls} unter Beibehaltung des ursprünglichen Dateinamens.
\item \textbf{FA-9 — ZIP-Download:} Mehrere Dokumente können über Auswahlboxen markiert und gemeinsam als ZIP-Archiv heruntergeladen werden. Die Ordnerstruktur des Archivs entspricht der S3-Struktur; das Archiv wird erst beim Klick erzeugt.
\item \textbf{FA-10 — PDF-Vorschau:} PDF-Dokumente können in einem Modal angezeigt werden, ohne zuvor heruntergeladen zu werden. Für Nicht-PDF-Dateien wird keine Vorschau geöffnet.
\item \textbf{FA-11 — Share-Links:} Für jedes Dokument kann ein Freigabelink erzeugt werden, dessen Zielseite OpenGraph-Meta-Tags für eine Linkvorschau bereitstellt und anschließend auf den Document Explorer weiterleitet.
\item \textbf{FA-12 — URL-Dateien:} Im Speicher abgelegte \texttt{.url}-Dateien werden nach dem INI-Format ausgewertet und leiten beim Anklicken auf die hinterlegte Adresse weiter. Ist keine gültige URL erkennbar, wird die Datei wie eine normale Datei behandelt.
\item \textbf{FA-13 — Automatische Ordneranlage:} Beim Aufruf der Dokumentenseite wird die vollständige Typordnerstruktur für die Organisation angelegt, sofern sie noch nicht existiert.
\end{itemize}
\subsection{Nichtfunktionale Anforderungen}
\begin{itemize}
\item \textbf{NFA-1 — Skalierbarkeit des Lookups:} Die Auflösung des Kundenordners darf nicht linear mit der Anzahl der Kunden wachsen. Im Normalfall soll ein einzelner Aufruf genügen.
\item \textbf{NFA-2 — Fehlerbehandlung:} Können Dokumente nicht geladen werden, wird eine verständliche Fehlermeldung angezeigt. Eine stillschweigend leere Seite ist nicht zulässig.
\item \textbf{NFA-3 — Leerer Zustand:} Existieren für einen berechtigten Benutzer keine Dokumente, wird ein entsprechender Hinweis angezeigt.
\item \textbf{NFA-4 — Konsistenz zur bestehenden Oberfläche:} Suche, Paginierung und Bedienelemente orientieren sich an den übrigen Houston-Seiten.
\item \textbf{NFA-5 — Testbarkeit:} Die Logik zur Auflösung des Kundenordners ist durch Unit-Tests mit einem gemockten \texttt{IAmazonS3} abgedeckt.
\item \textbf{NFA-6 — Pfadsicherheit:} Beim Download wird geprüft, dass der angeforderte Schlüssel innerhalb des Kundenordners liegt; Pfadanteile zum Verlassen des Ordners werden abgewiesen.
\end{itemize}
+14 -1
View File
@@ -1,4 +1,17 @@
\section{Zeit- und Aufwandsplanung}
\label{sec:schedule}
% TODO: Sprints 15–17.2026, Aufwandsschätzungen (Story Points), 24 Manntage / 192 h gesamt
Für das Praktikum sind 24 Manntage beziehungsweise 192 Arbeitsstunden vorgesehen. Der Projektzeitraum erstreckt sich von der Themenfindung Ende Mai 2026 bis zur Abgabe der Dokumentation. Die Umsetzung verteilt sich auf die Sprints 15.2026 bis 17.2026. Abbildung~\ref{fig:gantt} zeigt die geplante zeitliche Verteilung.
\begin{figure}[H]
\centering
\includegraphics[width=\textwidth]{figures/diagrams/gantt-plan.pdf}
\caption{Zeitplanung des Projekts}
\label{fig:gantt}
\end{figure}
Die Planung gliedert sich in vier Phasen. Die \textbf{Analyse- und Konzeptionsphase} (Ende Mai bis Anfang Juli) umfasste die Themenfindung mit \emph{Sarah Hinzmann} und \emph{Thomas Drewermann}, die Feature-Analyse sowie den Schnitt und die Freigabe der Product Backlog Items. Die \textbf{Implementierungsphase} begann am 27.~Juli mit dem ersten Pull Request und erstreckte sich über die Sprints 15 und 16. Parallel dazu lief die \textbf{Qualitätssicherung} in Form fortlaufender Code-Reviews; der Abnahmetest begann am 13.~August. Die \textbf{Abschlussphase} umfasst Release, Dokumentation und Präsentation.
Eine Besonderheit der Planung ist die Abhängigkeit von der Infrastrukturbereitstellung: Der Document Explorer konnte erst umgesetzt werden, nachdem der S3-Speicher zur Verfügung stand. Diese Abhängigkeit wurde bereits im Approval-Termin am 8.~Juli durch \emph{Stephan Janßen} als Voraussetzung am Item vermerkt. Der tatsächliche Vorlauf für die Beschaffung wird im folgenden Abschnitt beschrieben.
Der Soll-Ist-Vergleich der Zeitplanung findet sich in Abschnitt~\ref{sec:target-comparison}.
Binary file not shown.
+15 -11
View File
@@ -1,18 +1,22 @@
sequenceDiagram
participant B as Browser
autonumber
actor U as Benutzer
participant H as Houston
participant AAD as Entra ID
participant S3 as S3-Speicher
B->>H: GET /documents
H->>H: Prüfe Claim Documents.Read
alt Keine Rolle
H-->>B: 403 Forbidden
U->>H: GET /documents
H->>AAD: Authentifizierung (OpenID Connect)
AAD-->>H: Token mit Claims:<br/>roles, efecte:company_id,<br/>efecte:company_name
alt Rolle Documents.Read fehlt
H-->>U: 403 Forbidden<br/>(Menuepunkt bereits ausgeblendet)
else Rolle vorhanden
H->>H: Lese efecte:company_id aus Claim
H->>S3: ResolveCustomerPrefixAsync(orgId)
S3-->>H: Kundenordner-Prefix
H->>S3: ListObjectsV2(prefix)
S3-->>H: Objektliste
H-->>B: Documents-Seite
H->>H: orgId, orgName aus Claims lesen
H->>S3: ResolveCustomerPrefixAsync(orgId, orgName)
S3-->>H: Prefix des Kundenordners
H->>S3: ListObjectsV2(prefix, delimiter)
S3-->>H: Objektliste des Kunden
H->>H: Typ je Dokument aus<br/>Unterordner ableiten
H-->>U: Dokumentenliste
end
Binary file not shown.
File diff suppressed because it is too large Load Diff
+15 -9
View File
@@ -1,10 +1,16 @@
flowchart TD
A[ResolveCustomerPrefixAsync\norgId, orgName] --> B{Fast-Path:\nGetObjectMetadata\nslug-name/}
B -->|Gefunden & ID passt| Z[✓ Fertig\n1 Request]
B -->|Nicht gefunden oder\nID passt nicht| C{Kollisions-Fast-Path:\nslug-name orgId /}
C -->|Gefunden| Z2[✓ Fertig\n2 Requests]
C -->|Nicht gefunden| D[Fallback: O-n\nListObjectsV2 alle\nTop-Level-Prefixes]
D -->|Ordner mit\npAssender ID gefunden| E[Rename auf\nkanonischen Namen]
E --> Z3[✓ Fertig\nnächster Lookup O-1]
D -->|Kein Ordner| F[Anlegen: Ordner +\nUnterstruktur 9560]
F --> Z4[✓ Fertig\nSeite leer]
A["ResolveCustomerPrefixAsync(orgId, orgName)"] --> B["Fast-Path:<br/>GetObjectMetadata auf<br/>slug(name)/"]
B -->|"Marker vorhanden<br/>und org-id passt"| Z1["Fertig — 1 Request"]
B -->|"fehlt oder<br/>fremde org-id"| C["Kollisions-Fast-Path:<br/>GetObjectMetadata auf<br/>slug(name) (orgId)/"]
C -->|"Marker vorhanden<br/>und org-id passt"| Z2["Fertig — 2 Requests"]
C -->|"nicht gefunden"| D["Fallback O(n):<br/>alle Top-Level-Prefixes<br/>auflisten und über<br/>org-id filtern"]
D -->|"Ordner gefunden"| E["Rename auf<br/>kanonischen Namen"]
E --> Z3["Fertig — nächster<br/>Lookup ist O(1)"]
D -->|"kein Ordner"| F["Kundenordner samt<br/>Typ-Unterordnern anlegen"]
F --> Z4["Fertig — Seite<br/>zeigt leeren Zustand"]
style Z1 fill:#d5e8d4,stroke:#82b366
style Z2 fill:#d5e8d4,stroke:#82b366
style Z3 fill:#fff2cc,stroke:#d6b656
style Z4 fill:#fff2cc,stroke:#d6b656
style D fill:#f8cecc,stroke:#b85450
Binary file not shown.
+20 -7
View File
@@ -1,9 +1,22 @@
flowchart TD
B[(S3-Bucket\ndocuments-houston-prod)]
B --> KA["Kunde A/\n[meta: efecte_org-id=42]"]
B --> KB["Kunde B/\n[meta: efecte_org-id=77]"]
KA --> D1[doc1.pdf\nuntyped]
B["Bucket: documents-houston-prod"]
B --> KA["Beispielkunde GmbH/<br/><i>Marker-Objekt mit<br/>Metadatum efecte-org-id = 42</i>"]
B --> KB["Andere Firma AG/<br/><i>efecte-org-id = 77</i>"]
KA --> U1["Uebersicht.pdf<br/><i>ohne Typ</i>"]
KA --> T1["Service-Protokoll/"]
KA --> T2["Vertragsunterlagen/"]
T1 --> D2[bericht-2026-07.pdf]
T2 --> D3[SLA-2026.pdf]
KA --> T2["Monitoring Reports/"]
KA --> T3["Vertragsunterlagen/"]
KA --> T4["... weitere Typordner"]
T1 --> D1["Protokoll-2026-07.pdf"]
T2 --> D2["Report-Juli.pdf"]
T3 --> D3["Rahmenvertrag.pdf"]
T3 --> D4["Sharepoint-Ablage.url"]
style KA fill:#dae8fc,stroke:#6c8ebf
style KB fill:#dae8fc,stroke:#6c8ebf
style T1 fill:#fff2cc,stroke:#d6b656
style T2 fill:#fff2cc,stroke:#d6b656
style T3 fill:#fff2cc,stroke:#d6b656
style T4 fill:#fff2cc,stroke:#d6b656
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB