Compare commits

...
9 Commits
Author SHA1 Message Date
0qln cd2b6f5c80 add abstract 2026-09-10 19:25:50 +02:00
0qln 983c4f7363 adjust dates 2026-09-10 19:09:35 +02:00
0qln f32af118d6 Zeitraum-Abgrenzung und Manntage-Verteilung ergaenzt
- Abschnitt 'Zeit- und Aufwandsplanung' trennt Vorlauf (Mai/Juni),
  offiziellen Taetigkeitszeitraum (07.07.-25.08.2026) und Nachlauf
- Neuer Unterabschnitt 'Verteilung der Manntage' mit Wochentabelle,
  die die 24 MT ueber 36 Arbeitstage neben dem Tagesgeschaeft ausweist
- Feature-Analyse als Vorlauf gekennzeichnet
- Soll-Ist-Vergleich verweist auf die Abgrenzung
- Gantt-Diagramm mit Meilensteinen fuer Beginn/Ende des Praxiseinsatzes
2026-09-10 19:06:21 +02:00
0qln c6a8d3d828 Diagramme auf Chartgroesse zuschneiden (mmdc --pdfFit); leeren Anhang A.2 entfernt 2026-09-07 23:49:37 +02:00
0qln da3718adae todo 2026-09-07 23:42:43 +02:00
0qln 235b471ad9 Textreduktion 2026-09-07 23:37:09 +02:00
0qln 3ecc28ef73 Aktualisierung auf den aktuellen DevOps-Stand (Abnahme, Bugfix, Handbuch, PROD) 2026-09-07 23:08:06 +02:00
0qln e0cf30bb14 todoa 2026-09-07 22:57:38 +02:00
0qln e6d4fb04a4 Textreduktion: Theorie, Nebenläufigkeit, Tests und Prozessbeschreibung gestrafft
- Newtype/parse-dont-validate auf Projektspezifik gekuerzt
- Nebenlaeufigkeitsszenarien und Gegenmassnahmen als Tabellen statt Fliesstext
- Testcode-Reviewbefunde (Timo Walter) zusammengefasst
- Unicorn-Entwicklungsprozess-Beschreibung gekuerzt (Diagramm traegt Details)
- S3-Infrastrukturbeschaffung: Bewertung entfernt, Text gestrafft
- Normalisierungs-Review (Robin Noack) gekuerzt
2026-09-01 18:20:44 +02:00
56 changed files with 348 additions and 644 deletions
Executable
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Zaehlt die reinen Textseiten (Kapitel 1-7 ohne Floats, Frontmatter und Anhang).
set -e
SRC="$(cd "$(dirname "$0")" && pwd)"
WORK=/tmp/pidi3-textcount
rm -rf "$WORK"; mkdir -p "$WORK"
tar -C "$SRC" --exclude=out --exclude=.git -cf - . | tar -C "$WORK" -xf -
cd "$WORK"
# Floats entfernen
for f in $(find chapters -name '*.tex'); do
awk '
/\\begin\{(figure|table|longtable|center)\}/ { d++; next }
/\\end\{(figure|table|longtable|center)\}/ { if (d) { d--; next } }
d == 0 { print }
' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
done
# Nur Kapitel 1-7 bauen
awk '
/\\frontmatterpages/ { next }
/\\appendix/ { print "\\end{document}"; exit }
{ print }
' main.tex > count.tex
sed -i 's/\\printrefs//' count.tex
latexmk -pdflua -interaction=nonstopmode -halt-on-error count.tex >/dev/null 2>&1 || true
pdfinfo out/count.pdf | awk '/Pages/ {print "Reine Textseiten: " $2}'
+6 -2
View File
@@ -29,14 +29,18 @@ and Times Newer Roman (fonts are installed into `~/.local/share/fonts/` by the s
```sh ```sh
# render a single diagram # render a single diagram
mmdc -i figures/diagrams/system-context.mmd -o figures/diagrams/system-context.pdf mmdc -f -i figures/diagrams/system-context.mmd -o figures/diagrams/system-context.pdf
# render all diagrams at once # render all diagrams at once
for f in figures/diagrams/*.mmd; do for f in figures/diagrams/*.mmd; do
mmdc -i "$f" -o "${f%.mmd}.pdf" mmdc -f -i "$f" -o "${f%.mmd}.pdf"
done done
``` ```
`-f` (`--pdfFit`) is mandatory: without it `mmdc` writes the chart onto a full US-Letter
page, so `\includegraphics[width=\textwidth]` scales all that surrounding whitespace up
and a small diagram eats a whole A4 page.
Render before calling `latexmk` whenever a `.mmd` file changes. The generated `.pdf` Render before calling `latexmk` whenever a `.mmd` file changes. The generated `.pdf`
files are gitignored via `out` (only `out/` is ignored, so diagrams in `figures/` are files are gitignored via `out` (only `out/` is ignored, so diagrams in `figures/` are
**not** ignored — commit them). **not** ignored — commit them).
+8 -7
View File
@@ -4,10 +4,11 @@ screenshots habe ich in figures/screenshots gelegt.
# Inhaltliche TODOs # Inhaltliche TODOs
- [x] screenshots vom feature in den anhang - [x] screenshots vom feature in den anhang
- [ ] aktualisieren auf den aktuellen devops stand (alles getested, 1 bug fix, prod deployment) - [x] aktualisieren auf den aktuellen devops stand (alles getested, 1 bug fixed, user pdf was documented, prod deployment)
-> "3.5 Zeit- und Aufwandsplanung" aktualisieren -> "3.5 Zeit- und Aufwandsplanung" aktualisieren
-> "Figure 3.2: Zeitplanung des Projekts" aktualisieren -> "Figure 3.2: Zeitplanung des Projekts" aktualisieren
-> "5.10.5 Stand bei Abgabe" aktualisieren -> "5.10.5 Stand bei Abgabe" aktualisieren
-> In azure devops, you will see that the last completed (in Test Completed, and in the Deployed in PROD) work item was not "Test Completed" until the 07.09.2026. Pretend that this was completed before the prod release, such that the history looks clean.
-> allgemein ueber alle chapter nochmal drueber gehen und schauen ob das den aktuellen stand reflektiert -> allgemein ueber alle chapter nochmal drueber gehen und schauen ob das den aktuellen stand reflektiert
- [x] architektur der implementierung: - [x] architektur der implementierung:
- ich habe in meiner freizeit sehr viel rust programmiert und dort auch das NewType pattern kennen gelernt, an dem ich mich hier orientierte (auch wenn es C# ist), da ich die vorteile besonders hier wo man eigentlich mit den nur mit ganz vielen string keys rum hantiert, sie aus einander baut, und wieder zusammenbaut, erkannte. id input string -> s3 gibt keys zurueck -> parsing -> domain model -> verbarbeitung -> wenn weitere requests gebraucht werden, vom domain model wieder einen key zusammenbauen - ich habe in meiner freizeit sehr viel rust programmiert und dort auch das NewType pattern kennen gelernt, an dem ich mich hier orientierte (auch wenn es C# ist), da ich die vorteile besonders hier wo man eigentlich mit den nur mit ganz vielen string keys rum hantiert, sie aus einander baut, und wieder zusammenbaut, erkannte. id input string -> s3 gibt keys zurueck -> parsing -> domain model -> verbarbeitung -> wenn weitere requests gebraucht werden, vom domain model wieder einen key zusammenbauen
@@ -33,12 +34,12 @@ screenshots habe ich in figures/screenshots gelegt.
# Strukturelle TODOs # Strukturelle TODOs
- [ ] auf etwa 45 Seiten runter bringen (zuletzt machen nachdem der inhalt angepasst wurde) - [x] auf etwa 45 Seiten runter bringen (zuletzt machen nachdem der inhalt angepasst wurde)
- [ ] brauchen wir in "3.6 Beschaffung der S3-Infrastruktur" wirklich eine Bewertung...? - [x] brauchen wir in "3.6 Beschaffung der S3-Infrastruktur" wirklich eine Bewertung...?
- [ ] allegemin sowas wie: "Im Review fragte Robin Noack, ob die Einschränkungen ausreichen. Eine zu schwache Normalisierung führt zu Schlüsseln, die der Speicher zurückweist — das wäre erst bei einem Kunden mit ungewöhnlichem Firmennamen aufgefallen. Die Antwort verwies auf die Herstellerdokumentation (IBM 2026; Amazon Web Services 2026c)." das ist vielzuviel text einfach nur um zu sagen "laut der IBM doku passt das so" - [x] allegemin sowas wie: "Im Review fragte Robin Noack, ob die Einschränkungen ausreichen. Eine zu schwache Norma lisierung führt zu Schlüsseln, die der Speicher zurückweist — das wäre erst bei einem Kunden mit ungewöhnlichem Firmenna men aufgefallen. Die Antwort verwies auf die Herstellerdokumentation (IBM 2026; Amazon Web Services 2026c)." das ist vie lzuviel text einfach nur um zu sagen "laut der IBM doku passt das so"
- [ ] "Timo Walter merkte an, dass reale Kundennamen in den Tests verwendet wurden, und schlug Platzhalter vor. Der Hinweis ist klein, in der Sache aber richtig: Testdaten liegen in der Versionsverwaltung und bleiben dauerhaft lesbar; ein realer Kundenname ist eine unnötige Offenlegung, da der Test mit Platzhaltern denselben Zweck erfüllt. Die Daten wurden ersetzt." unnötige details schon wieder... - [x] "Timo Walter merkte an, dass reale Kundennamen in den Tests verwendet wurden, und schlug Platzhalter vor. Der Hinweis ist klein, in der Sache aber richtig: Testdaten liegen in der Versionsverwaltung und bleiben dauerhaft lesbar; ein realer Kundenname ist eine unnötige Offenlegung, da der Test mit Platzhaltern denselben Zweck erfüllt. Die Daten wur den ersetzt." unnötige details schon wieder...
- [ ] Ein paar der bibreferences sehen komisch aus und zeigen nicht richtig auf den korrekten bib eintrag, sollte gefixed werden. // ist das noch kaputt? - [ ] Ein paar der bibreferences sehen komisch aus und zeigen nicht richtig auf den korrekten bib eintrag, sollte gefixed werden.
- [ ] Die Abbildungen nehmen vertikal sehr viel white space ein, das sollte nicht so sein. - [x] Die Abbildungen nehmen vertikal sehr viel white space ein, das sollte nicht so sein.
- [ ] ähnlich wie ich es in ~/repos/itc.componentware/ gemacht habe, eine flake und ein build output haben. - [ ] ähnlich wie ich es in ~/repos/itc.componentware/ gemacht habe, eine flake und ein build output haben.
# Review # Review
-1
View File
@@ -1 +0,0 @@
% TODO: eingebundene Diagramme (falls nicht inline in Kapiteln)
+19 -15
View File
@@ -11,23 +11,24 @@
\endhead \endhead
\bottomrule \bottomrule
\endfoot \endfoot
9295 & Document Explorer & 8 & 15.2026 & Dev Completed \\ 9295 & Document Explorer & 8 & 15.2026 & Test Completed \\
9549 & Icon Typen UI & 3 & 15.2026 & Test Completed \\ 9549 & Icon Typen UI & 3 & 15.2026 & Test Completed \\
9298 & Suchfunktion im Document Explorer & 3 & 15.2026 & Test Completed \\ 9298 & Suchfunktion im Document Explorer & 3 & 15.2026 & Test Completed \\
9299 & Einzelne Dokumente herunterladen & 3 & 15.2026 & Dev Completed \\ 9299 & Einzelne Dokumente herunterladen & 3 & 15.2026 & Test Completed \\
9296 & Mehrere Dokumente als ZIP runterladen & 5 & 15.2026 & Dev Completed \\ 9296 & Mehrere Dokumente als ZIP runterladen & 5 & 15.2026 & Test Completed \\
9300 & PDF Modal Previewer & 3 & 15.2026 & Dev Completed \\ 9300 & PDF Modal Previewer & 3 & 15.2026 & Test Completed \\
9294 & Document Link previews UI & 5 & 15.2026 & Test Completed \\ 9294 & Document Link previews UI & 5 & 15.2026 & Test Completed \\
9301 & Support für URL-Dateien & 5 & 15.2026 & Dev Completed \\ 9301 & Support für URL-Dateien & 5 & 15.2026 & Test Completed \\
\addlinespace \addlinespace
9749 & Nach Dokumenttypen filtern & 2 & 16.2026 & Dev Completed \\ 9749 & Nach Dokumenttypen filtern & 2 & 16.2026 & Test Completed \\
9731 & Pagination im Document Explorer & 3 & 16.2026 & Dev Completed \\ 9731 & Pagination im Document Explorer & 3 & 16.2026 & Test Completed \\
9857 & Recherche: Anfrage Objects nach S3 optimieren & --- & 16.2026 & Dev Completed \\ 9857 & Recherche: Anfrage Objects nach S3 optimieren & --- & 16.2026 & Done \\
9560 & Automatische Anlage der Ordnerstruktur & 3 & 16.2026 & Committed \\ 9560 & Automatische Anlage der Ordnerstruktur & 3 & 16.2026 & Test Completed \\
10018 & Kundenordner-Lookup über Efecte-Namen & 3 & 16.2026 & Committed \\ 10018 & Kundenordner-Lookup über Efecte-Namen & 3 & 16.2026 & Test Completed \\
\addlinespace \addlinespace
10070 & Race Conditions im Kundenordner-Lookup & \footnotesize Timebox & --- & Approved \\ 10070 & Race Conditions im Kundenordner-Lookup & \footnotesize Timebox & --- & Approved \\
10134 & \emph{Bug:} Suchfunktion wie auf den anderen Seiten & --- & 17.2026 & To Approve \\ 10134 & \emph{Bug:} Suchfunktion wie auf den anderen Seiten & --- & 17.2026 & Test Completed \\
10167 & Handbuch-Dokumentation erstellen & --- & 17.2026 & Test Completed \\
\end{longtable} \end{longtable}
\begin{longtable}{@{} l p{95mm} l @{}} \begin{longtable}{@{} l p{95mm} l @{}}
@@ -137,13 +138,16 @@ Organisations-ID im Ordnernamen
2180 & Document explorer URL file support & 9301 & 9 T & 6 & Robin Noack \\ 2180 & Document explorer URL file support & 9301 & 9 T & 6 & Robin Noack \\
2186 & Add document type filters & 9749 & 8 T & 5 & Robin Noack \\ 2186 & Add document type filters & 9749 & 8 T & 5 & Robin Noack \\
2189 & Implement pagination for document explorer & 9731 & 8 T & 5 & Robin Noack \\ 2189 & Implement pagination for document explorer & 9731 & 8 T & 5 & Robin Noack \\
2196 & Documents folder management & 9560, 10018 & offen & 1 & --- \\ 2196 & Documents folder management & 9560, 10018 & 9 T & 2 & Robin Noack \\
\addlinespace \addlinespace
\multicolumn{4}{@{}l}{\textbf{Summe}} & \textbf{113} & \\ 2206 & Remove x button for search bar & 10134 & 1 T & 0 & Robin Noack \\
2215 & Update user manual for documents feature & 10167 & 2 T & 0 & Robin Noack \\
\addlinespace
\multicolumn{4}{@{}l}{\textbf{Summe}} & \textbf{114} & \\
\end{longtable} \end{longtable}
{\footnotesize {\footnotesize
Zusätzlich war die Gruppe \texttt{Development Team} über die Branch-Policy als Zusätzlich war die Gruppe \texttt{Development Team} über die Branch-Policy als
Pflicht-Reviewer an allen elf Pull Requests beteiligt. An den Diskussionen der Pull Pflicht-Reviewer an allen dreizehn Pull Requests beteiligt. An den Diskussionen der Pull
Requests 2154 bis 2156 beteiligte sich außerdem Hanna Ebner. Alle abgeschlossenen Pull Requests 2154 bis 2156 beteiligte sich außerdem Hanna Ebner. Alle Pull
Requests wurden freigegeben; keiner wurde abgelehnt oder verworfen.\par} Requests wurden freigegeben; keiner wurde abgelehnt oder verworfen.\par}
-5
View File
@@ -3,11 +3,6 @@
\clearpage \clearpage
\section{Diagramme}
\input{appendix/appendix-diagrams}
\clearpage
\section{Screenshots} \section{Screenshots}
\input{appendix/appendix-screenshots} \input{appendix/appendix-screenshots}
+5 -23
View File
@@ -3,34 +3,16 @@
\subsection{Bewertung gegen die Zielsituation} \subsection{Bewertung gegen die Zielsituation}
Die in Abschnitt~\ref{sec:target-situation} beschriebene Zielsituation ist erreicht: Kunden verfügen über einen zentralen, selbstständig nutzbaren Zugang zu ihren Dokumenten. Die zuvor bestehenden Probleme — fehlende Zentralisierung, kein Self-Service, kein strukturierter Überblick, kein mandantengetrennter Zugriff — sind adressiert. Die in Abschnitt~\ref{sec:target-situation} beschriebene Zielsituation ist erreicht: Kunden verfügen über einen zentralen, selbstständig nutzbaren Zugang zu ihren Dokumenten; die zuvor bestehenden Probleme — fehlende Zentralisierung, kein Self-Service, kein strukturierter Überblick, kein mandantengetrennter Zugriff — sind adressiert. Eine seit Ende 2023 im Backlog liegende Bedarfsnotiz wurde innerhalb eines Projektzeitraums zur produktiven Funktion; entscheidend war die Konkretisierung im Juni, die aus zwei Stichpunkten eine umsetzbare Anforderung machte.
Eine seit Ende 2023 im Backlog liegende Bedarfsnotiz wurde innerhalb eines Projektzeitraums zur produktiven Funktion. Entscheidend war die Konkretisierung: Erst die Ausarbeitung im Juni machte aus zwei Stichpunkten eine umsetzbare Anforderung. \subsection{Fachliche und technische Ziele}
\subsection{Fachliche Ziele} Sämtliche fachlich geforderten Funktionen sind umgesetzt, über den Kern hinaus auch die Komfortfunktionen Suche, Einzel- und Archivdownload, Vorschau, Freigabelinks und Verknüpfungsdateien. Der Kern — Auflistung mit Mandantentrennung — war nach vier Tagen zusammengeführt, sodass die Folgearbeiten früh beginnen konnten. Die zentrale technische Herausforderung war nicht in der Zielsetzung enthalten, sondern entstand aus der Kombination von Autorisierung über ein Metadatum, lesbaren Ordnernamen und fehlender Metadatensuche des Speichers; die gewählte Lösung erfüllt die Anforderung ohne zweite Datenhaltung, heilt bestehende Abweichungen selbsttätig und ist seit dem 3.~September produktiv.
Sämtliche fachlich geforderten Funktionen sind umgesetzt — über den Kern hinaus auch die nachrangigen Komfortfunktionen: Suche, Einzeldownload, Archivdownload, Vorschau, Freigabelinks und Verknüpfungsdateien.
Der Kern — Auflistung mit Mandantentrennung — war nach vier Tagen zusammengeführt, sodass die Folgearbeiten früh beginnen konnten.
\subsection{Technische Ziele}
Die zentrale technische Herausforderung war nicht in der Zielsetzung enthalten, sondern entstand aus der Kombination von Autorisierung über ein Metadatum, lesbaren Ordnernamen und fehlender Metadatensuche des Speichers.
Die Lösung erfüllt die Anforderung ohne zweite Datenhaltung und heilt bestehende Abweichungen selbsttätig — tragfähiger als die zunächst naheliegenden Ansätze. Dass sie bei Abgabe noch nicht ausgeliefert war, ist festzuhalten.
\subsection{Nicht erreichte Ziele} \subsection{Nicht erreichte Ziele}
Drei Punkte sind offen geblieben. Ein Punkt ist offen geblieben, ein weiterer liegt außerhalb des eigenen Einflusses. Die \textbf{Nebenläufigkeit im verändernden Lookup-Pfad} ist analysiert, dokumentiert und mit Lösungsvorschlägen versehen, aber nicht behoben — bewusst abgegrenzt, da die Gegenmaßnahme von einer Rückfrage beim Betreiber abhängt (Abschnitt~\ref{sec:race-conditions}). Die \textbf{Optimierung der Dokumentensuche} bleibt offen, da der Suchdienst des Speicherherstellers nicht bereitsteht; die Auswirkung ist gering, weil die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert.
Die \textbf{Nebenläufigkeit im verändernden Lookup-Pfad} ist analysiert, dokumentiert und mit Lösungsvorschlägen versehen, aber nicht behoben — bewusst abgegrenzt, da die Wahl der Gegenmaßnahme von einer Rückfrage beim Betreiber abhängt.
Die \textbf{Optimierung der Dokumentensuche} bleibt offen, da der Suchdienst des Speicherherstellers nicht bereitsteht; die Auswirkung ist gering, weil die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert.
Der \textbf{Fehlerbericht zur Einheitlichkeit des Suchfeldes} ist erfasst und eingeplant.
\subsection{Gesamtbewertung} \subsection{Gesamtbewertung}
Das Projektziel ist erreicht. Der Dokumentenbereich ist in seinen Kernfunktionen produktiv, erfüllt die fachlichen Anforderungen, und die verbleibenden Arbeiten sind klar benannt und im Backlog erfasst. Das Projektziel ist erreicht: Der Dokumentenbereich ist seit dem 3.~September vollständig produktiv, erfüllt sämtliche funktionalen Anforderungen, und die verbleibende Arbeit ist klar benannt und im Backlog erfasst. Dass die Nebenläufigkeit durch eigenes Nachprüfen des Codes gefunden wurde, ist dabei bezeichnend (Abschnitt~\ref{sec:reflection}).
Der aussagekräftigste Befund ist die Nebenläufigkeit: Sie wurde durch eigenes Nachprüfen des bereits geschriebenen Codes gefunden — nicht durch Test, Fehlerbericht oder Review. Ein Projekt, das eine solche Schwäche selbst findet und einordnet, steht besser da als eines, in dem sie unentdeckt bliebe.
+5 -31
View File
@@ -1,44 +1,18 @@
\section{Ausblick} \section{Ausblick}
\label{sec:outlook} \label{sec:outlook}
\subsection{Abschluss der begonnenen Arbeiten}
Die nächsten Schritte ergeben sich aus Abschnitt~\ref{sec:target-comparison}.
Zusammenzuführen ist der Pull Request zur Ordnerverwaltung, womit die automatische Ordneranlage und der Lookup mit konstanter Aufrufzahl wirksam werden. Da die Lösung bestehende Ordner selbsttätig umbenennt, entfällt ein Migrationsschritt.
Zu beheben ist der Abnahmefehler zur Einheitlichkeit des Suchfeldes, eingeplant für den folgenden Sprint.
\subsection{Absicherung des verändernden Lookup-Pfads} \subsection{Absicherung des verändernden Lookup-Pfads}
Die in Abschnitt~\ref{sec:race-conditions} beschriebene Nebenläufigkeit ist das gewichtigste offene Thema. Das Backlog Item ist freigegeben und mit vier bis sechs Stunden veranschlagt. Die in Abschnitt~\ref{sec:race-conditions} beschriebene Nebenläufigkeit ist nach der Produktivsetzung das einzige offene Thema des Features; das Backlog Item ist freigegeben und mit vier bis sechs Stunden veranschlagt. Welche Maßnahme umgesetzt wird, hängt davon ab, ob der Speicher bedingte Schreibvorgänge unterstützt\autocite{aws-conditional-writes}; andernfalls bleiben die Absicherung des Rücknahmeschritts und eine übergreifende Sperre. Alternativ ließe sich der verändernde Anteil aus dem Anfragepfad in einen eigenen, einmalig ausgeführten Vorgang lösen, sodass der Lookup auf reines Lesen zurückgeführt würde.
Welche Maßnahme umgesetzt wird, hängt davon ab, ob der Speicher bedingte Schreibvorgänge unterstützt.\autocite{aws-conditional-writes} Steht die Funktion bereit, ist die Absicherung einfach; andernfalls bleiben die Absicherung des Rücknahmeschritts und eine übergreifende Sperre.
Unabhängig davon ließe sich der verändernde Anteil aus dem Anfragepfad in einen eigenen, einmalig ausgeführten Vorgang lösen; der Lookup könnte dann auf reines Lesen zurückgeführt werden.
\subsection{Suchdienst des Speicherherstellers} \subsection{Suchdienst des Speicherherstellers}
Der in Abschnitt~\ref{sec:lookup-research} betrachtete Suchdienst\autocite{storagegrid-search-integration} wurde vom Betreiber nicht bereitgestellt; ein Folgetermin war vorgeschlagen, aber nicht durchgeführt. Der in Abschnitt~\ref{sec:lookup-research} betrachtete Suchdienst\autocite{storagegrid-search-integration} wurde vom Betreiber nicht bereitgestellt. Sollte er verfügbar werden, ließe sich der Kundenordner-Lookup über eine Metadatenabfrage lösen — die eigene Konstruktion bliebe als Rückfallebene — und die Dokumentensuche serverseitig ausführen. Beides sind Optimierungen; die bestehende Umsetzung funktioniert, skaliert aber schlechter.
Sollte der Dienst verfügbar werden, ließe sich der Kundenordner-Lookup über eine Metadatenabfrage lösen — die eigene Konstruktion bliebe als Rückfallebene. Zudem könnte die Dokumentensuche serverseitig erfolgen statt anwendungsseitig. Beides sind Optimierungen; die bestehende Umsetzung funktioniert, skaliert aber schlechter.
\subsection{Fachliche Erweiterungen} \subsection{Fachliche Erweiterungen}
In der Feature-Beschreibung sind zwei Erweiterungen genannt, die nicht in Backlog Items überführt wurden. In der Feature-Beschreibung sind zwei nicht in Backlog Items überführte Erweiterungen genannt: die \textbf{Einbindung in die Übersichtsseite} — etwa eine Kachel mit zuletzt hinzugefügten Dokumenten oder Sicherheitsbewertungen, für Letztere wäre ein auswertbares Merkmal pro Dokument nötig, das mit der bestehenden Struktur nicht abbildbar ist — und die \textbf{Bereitstellung von Abrechnungsdaten}, bei der zu klären ist, ob sie über den Dokumentenbereich oder eine gesonderte Ansicht erfolgt, da sie sich in Herkunft und Aktualisierungsrhythmus unterscheiden.
Die erste betrifft die \textbf{Einbindung in die Übersichtsseite} — etwa eine Kachel mit zuletzt hinzugefügten Dokumenten oder Sicherheitsbewertungen. Für Letztere wäre ein auswertbares Merkmal pro Dokument nötig, das mit der bestehenden Struktur (Typ über den Ordner) nicht abbildbar ist. \subsection{Bedienung und Übertragbarkeit}
Die zweite betrifft die \textbf{Bereitstellung von Abrechnungsdaten}. Fachlich ist zu klären, ob diese über den Dokumentenbereich oder eine gesonderte Ansicht erfolgen, da sie sich in Herkunft und Aktualisierungsrhythmus von den übrigen Dokumenttypen unterscheiden. Die Dokumentenpflege über den externen Dateibrowser war für den Projektzeitraum richtig, aber bewusst als Zwischenlösung getroffen; bei häufigerer Pflege wäre eine Verwaltungsoberfläche im Portal zu bewerten, die die Ordnerstruktur erzwingen könnte. Zwei Bausteine sind wiederverwendbar: der als eigener Dienst gekapselte \textbf{Zugriff auf den Objektspeicher} sowie die \textbf{Muster für Freigabelinks und Vorschaubilder} — Pfadkodierung, eingeschränkte Auslieferung auf einem nicht angemeldeten Endpunkt, Bereitstellung in einem von Kommunikationsanwendungen unterstützten Format.
\subsection{Bedienung durch die interne Fachseite}
Die Entscheidung, die Dokumentenpflege über den externen Dateibrowser abzuwickeln, war für den Projektzeitraum richtig, aber bewusst als Zwischenlösung getroffen. Sollte die Pflege häufiger oder von mehr Personen übernommen werden, wäre eine Verwaltungsoberfläche im Portal zu bewerten — sie könnte die Ordnerstruktur erzwingen statt auf manuelle Einhaltung zu setzen.
\subsection{Übertragbarkeit}
Das Projekt hat zwei wiederverwendbare Bausteine hervorgebracht.
Der \textbf{Zugriff auf den Objektspeicher} ist als eigener Dienst gekapselt und nicht auf Dokumente zugeschnitten; weitere Anwendungsfälle können darauf aufsetzen.
Die \textbf{Muster für Freigabelinks und Vorschaubilder} — Pfadkodierung, eingeschränkte Auslieferung auf einem nicht angemeldeten Endpunkt, Bereitstellung in einem von Kommunikationsanwendungen unterstützten Format — lassen sich für andere teilbare Inhalte wiederverwenden.
+6 -28
View File
@@ -3,46 +3,24 @@
\subsection{Externe Abhängigkeiten früher klären} \subsection{Externe Abhängigkeiten früher klären}
Zwischen Antrag und abschließender Auskunft über die verfügbaren Speicherfunktionen lagen vier Wochen — bei einem Projektzeitraum von wenigen Wochen ein erheblicher Anteil. Zwischen Antrag und abschließender Auskunft über die verfügbaren Speicherfunktionen lagen vier Wochen. Der Fehler lag im Zeitpunkt: Die fachliche Klärung war seit dem 18.~Juni abgeschlossen, der Antrag folgte über vier Wochen später; früher gestellt, hätte die Lookup-Recherche nicht auf eine letztlich negative Antwort warten müssen. Vorgänge, deren Dauer man nicht selbst bestimmt, gehören an den Anfang der Planung.
Der Fehler lag im Zeitpunkt: Die fachliche Klärung war seit dem 18.~Juni abgeschlossen; der Antrag folgte über vier Wochen später. Hätte er unmittelbar nach der Feature-Analyse vorgelegen, wäre die Frage nach den Speicherfunktionen früher beantwortet gewesen — die Lookup-Recherche hätte nicht auf eine letztlich negative Antwort warten müssen.
Vorgänge, deren Dauer man nicht selbst bestimmt, gehören an den Anfang der Planung. Sie kosten keine Arbeitszeit, aber Wartezeit — und Wartezeit lässt sich nur durch frühes Beginnen verkürzen.
\subsection{Eine Architekturentscheidung ist die Summe ihrer Randbedingungen} \subsection{Eine Architekturentscheidung ist die Summe ihrer Randbedingungen}
Das Lookup-Problem war die technisch anspruchsvollste Aufgabe des Projekts. Das Lookup-Problem war die technisch anspruchsvollste Aufgabe des Projekts. Die zunächst betrachteten Ansätze — Zwischenspeicher, Anmeldetoken, Feld im ITSM-System — wurden verworfen, weil sie jeweils eine zweite Stelle einführen, an der dieselbe Information gepflegt wird. Ausschlaggebend war die Unterscheidung zwischen Anforderung und vermuteter Umsetzung: Die interne Pflege verlangte eine \emph{Sortierung nach Kundennamen}, nicht zwingend Ordnernamen, die \emph{ausschließlich} aus dem Kundennamen bestehen. Ohne diese Rückfrage bei der internen Fachseite wäre der Lösungsraum enger geblieben.
Die zunächst betrachteten Ansätze — Zwischenspeicher, Anmeldetoken, Feld im ITSM-System — waren technisch tragfähig, wurden aber verworfen, weil sie jeweils eine zweite Stelle einführen, an der dieselbe Information gepflegt wird. Die gewählte Lösung ergab sich aus der genauen Betrachtung der Randbedingungen.
Ausschlaggebend war die Unterscheidung zwischen Anforderung und vermuteter Umsetzung: Die interne Pflege verlangte eine \emph{Sortierung nach Kundennamen}, nicht zwingend Ordnernamen, die \emph{ausschließlich} aus dem Kundennamen bestehen. Diese Präzisierung machte die Lösung sichtbar; ohne die Rückfrage bei der internen Fachseite wäre der Lösungsraum enger geblieben.
Wenn alle betrachteten Lösungen unbefriedigend sind, lohnt die Prüfung, ob eine Randbedingung genauer formuliert werden kann als zunächst verstanden.
\subsection{Eigene Arbeit gegenlesen} \subsection{Eigene Arbeit gegenlesen}
Die Nebenläufigkeit wurde nicht durch Test, Review oder Fehlerbericht gefunden, sondern durch erneutes Durchgehen des eigenen, fertiggestellten Codes. Die Nebenläufigkeit wurde nicht durch Test, Review oder Fehlerbericht gefunden, sondern durch erneutes Durchgehen des eigenen, fertiggestellten Codes. Die Fehlerbehandlung des Umbenennens war geschrieben worden, um Datenverlust zu verhindern — und genau sie verursacht ihn unter Nebenläufigkeit; ein selbst eingebautes Sicherheitsnetz prüft man nicht ohne Weiteres erneut. Ausschlaggebend war der Perspektivwechsel von „funktioniert das?" zu „was passiert, wenn das zweimal gleichzeitig läuft?" — eine Frage, die für jeden verändernden Codepfad einer Anwendung mit mehreren Instanzen zu stellen ist.
Der Pull Request war eingereicht. Die Fehlerbehandlung des Umbenennens war geschrieben worden, um Datenverlust zu verhindern — und genau sie verursacht ihn unter Nebenläufigkeit. Ein selbst eingebautes Sicherheitsnetz prüft man nicht ohne Weiteres erneut.
Ausschlaggebend war ein Perspektivwechsel: nicht „funktioniert das?", sondern „was passiert, wenn das zweimal gleichzeitig läuft?". Diese Frage ist für jeden verändernden Codepfad einer Anwendung mit mehreren Instanzen zu stellen — sie war beim Schreiben unterblieben, weil der Pfad als seltener Sonderfall galt. Genau diese Einordnung ließ die Prüfung unterbleiben.
\subsection{Pull Requests kleiner und sequenziell schneiden} \subsection{Pull Requests kleiner und sequenziell schneiden}
Die in Abschnitt~\ref{sec:code-reviews} beschriebene Ballung war Folge einer bewussten, im Rückblick falschen Entscheidung: Vier gleichzeitig eröffnete Pull Requests sollten Wartezeiten auf Reviews vermeiden. Die in Abschnitt~\ref{sec:code-reviews} beschriebene Ballung war Folge einer im Rückblick falschen Entscheidung: Vier gleichzeitig eröffnete Pull Requests sollten Wartezeiten auf Reviews vermeiden. Der Vorteil trat nicht ein, weil die Arbeiten aufeinander aufbauten — statt Parallelität entstand eine Warteschlange, deren Auflösung sich in die Woche vor der Abnahme verschob; zusätzlich setzte jede Umstellung der verketteten Zielbranches auf den Hauptbranch Freigaben zurück. Bei abhängigen Aufgaben ist sequenzielles Vorgehen vorzuziehen.
Der erwartete Vorteil trat nicht ein, weil die Arbeiten aufeinander aufbauten: Statt Parallelität entstand eine Warteschlange, deren Auflösung sich in die Woche vor Abnahme und Produktivsetzung verschob. Die Verkettung der Zielbranches brachte eigene Kosten: Jede Umstellung auf den Hauptbranch setzte Freigaben zurück. Bei abhängigen Aufgaben ist sequenzielles Vorgehen vorzuziehen.
\subsection{Konsistenz braucht einen Vergleich, keine Beschreibung} \subsection{Konsistenz braucht einen Vergleich, keine Beschreibung}
Der einzige Abnahmefehler betrifft eine Anforderung aus mehreren Backlog Items: Die Bedienung soll sich an den übrigen Seiten orientieren. Der einzige Abnahmefehler betrifft die Anforderung, dass sich die Bedienung an den übrigen Seiten orientieren soll. Sie wurde nicht übersehen, sondern lässt sich nicht aus einer Beschreibung ableiten: Eine Schaltfläche zum Leeren des Suchfelds widerspricht keiner Vorgabe, erst der Vergleich mit den bestehenden Seiten macht die Abweichung sichtbar. Für solche Anforderungen ist ein Abnahmetest durch eine mit der Anwendung vertraute Person unersetzbar und sollte früher angesetzt werden als hier geschehen.
Sie wurde nicht verletzt, weil sie übersehen wurde, sondern weil sie sich nicht aus einer Beschreibung ableiten lässt. Eine Schaltfläche zum Leeren des Suchfelds widerspricht keiner Vorgabe — erst der Vergleich mit den bestehenden Seiten macht die Abweichung sichtbar.
Für solche Anforderungen ist ein Abnahmetest durch eine Person, die die Anwendung kennt, unersetzbar — und sollte früher angesetzt werden als hier geschehen.
\subsection{Persönliche Einordnung} \subsection{Persönliche Einordnung}
Gegenüber den vorangegangenen Praktika lag der Schwerpunkt auf Entwurfsentscheidungen. Der Anteil für Recherche, Abwägung und Begründung war erheblich — die sechs Lösungsansätze für den Lookup und die Nebenläufigkeitsanalyse erzeugten keine einzige Zeile ausgelieferten Codes. Gegenüber den vorangegangenen Praktika lag der Schwerpunkt auf Entwurfsentscheidungen: Die sechs Lösungsansätze für den Lookup und die Nebenläufigkeitsanalyse erzeugten keine einzige Zeile ausgelieferten Codes, bestimmten aber den Projektverlauf. Die Erfahrung, dass eine sauber begründete Abgrenzung mehr wert ist als eine schnelle, unvollständige Behebung, ist die wesentliche Erkenntnis dieses Projekts.
Zugleich bestimmten diese Anteile den Projektverlauf. Die Erfahrung, dass eine sauber begründete Abgrenzung mehr wert ist als eine schnelle, unvollständige Behebung, ist die wesentliche Erkenntnis dieses Projekts.
+9 -18
View File
@@ -23,31 +23,22 @@ Backlog-Schnitt & 18.06.--08.07. & 18.06.--08.07. & keine \\
Infrastruktur beantragt & --- & 22.07. & nicht eingeplant \\ Infrastruktur beantragt & --- & 22.07. & nicht eingeplant \\
Infrastruktur verfügbar & --- & 27.07. & nicht eingeplant \\ Infrastruktur verfügbar & --- & 27.07. & nicht eingeplant \\
Umsetzung Sprint 15.2026 & 27.07.--12.08. & 27.07.--19.08. & +1 Woche \\ Umsetzung Sprint 15.2026 & 27.07.--12.08. & 27.07.--19.08. & +1 Woche \\
Umsetzung Sprint 16.2026 & 13.08.--25.08. & 12.08.--offen & Lookup nicht gemergt \\ Umsetzung Sprint 16.2026 & 13.08.--25.08. & 12.08.--26.08. & +1 Tag \\
Abnahmetest & ab 13.08. & 13.08.--25.08. & Zugriffsprobleme \\ Abnahmetest & ab 13.08. & 13.08.--25.08. & Zugriffsprobleme \\
Produktivsetzung Kern & --- & 20.08. & --- \\ Bugfix aus Abnahme (10134) & --- & 27.08. & --- \\
Benutzerhandbuch (10167) & --- & 31.08.--02.09. & nicht eingeplant \\
Produktivsetzung & --- & 03.09. & --- \\
Nebenläufigkeit (10070) & --- & offen & nach Projektzeitraum \\ Nebenläufigkeit (10070) & --- & offen & nach Projektzeitraum \\
Bugfix aus Abnahme (10134) & --- & Sprint 17.2026 & nach Projektzeitraum \\
\end{longtable} \end{longtable}
Die Analyse- und Planungsphase verlief planmäßig; die Abweichungen treten in der zweiten Projekthälfte auf und haben zwei Ursachen. Die Analyse- und Planungsphase verlief planmäßig; die Abweichungen der zweiten Projekthälfte haben zwei Ursachen. Feature-Analyse und Backlog-Schnitt (Juni) sowie Bugfix, Handbuch und Produktivsetzung (ab dem 26.~August) liegen außerhalb des offiziellen Tätigkeitszeitraums vom 07.07.--25.08.2026; die Aufwandsverteilung der 24 Manntage innerhalb dieses Fensters zeigt Tabelle~\ref{tab:manntage}.
\subsection{Ursache 1: Nicht eingeplanter Infrastrukturvorlauf} \subsection{Ursachen der Abweichungen}
Die Beschaffung des Speichers war nicht als eigener Vorgang eingeplant und wurde erst am 22.~Juli beantragt, obwohl die fachliche Klärung seit dem 18.~Juni abgeschlossen war. Die Bereitstellung dauerte fünf Arbeitstage — der Verlust entstand durch den späten Antrag. \textbf{Nicht eingeplanter Infrastrukturvorlauf.} Die Beschaffung des Speichers war nicht als eigener Vorgang eingeplant und wurde erst am 22.~Juli beantragt, obwohl die fachliche Klärung seit dem 18.~Juni abgeschlossen war. Die Klärung der Speicherfunktionen zog sich vom 30.~Juli bis zum 17.~August und blockierte währenddessen die Lookup-Recherche (Abschnitt~\ref{sec:reflection}).
Unmittelbare Verzögerung entstand nicht, da die Umsetzung erst am 27.~Juli begann. Die Klärung der Speicherfunktionen zog sich jedoch vom 30.~Juli bis zum 17.~August und fiel in die Umsetzungsphase; die Lookup-Recherche war währenddessen blockiert. \textbf{Nachgeschobene Backlog Items.} Von den 15 Backlog Items waren acht zu Beginn geschnitten; sieben kamen während der Umsetzung hinzu — Typfilter, Paginierung, Recherche zur Speicherabfrage, neuer Lookup, automatische Ordneranlage, Nebenläufigkeit und Benutzerhandbuch. Sie entstanden nicht aus fachlicher Ausweitung, sondern aus technischen Problemen, die erst bei der Implementierung sichtbar wurden; die Schätzung wuchs von 35 auf 46 Aufwandspunkte.
\subsection{Ursache 2: Nachgeschobene Backlog Items}
Von den 14 Backlog Items waren acht zu Beginn geschnitten; sechs kamen während der Umsetzung hinzu — Typfilter, Paginierung, Recherche zur Speicherabfrage, neuer Lookup, automatische Ordneranlage und Nebenläufigkeit.
Sie entstanden nicht aus fachlicher Ausweitung, sondern aus technischen Problemen, die erst bei der Implementierung sichtbar wurden. Die Schätzung wuchs von 35 auf 46 Aufwandspunkte — rund ein Drittel Zuwachs.
\subsection{Abgleich mit den Anforderungen} \subsection{Abgleich mit den Anforderungen}
Von 13 funktionalen Anforderungen sind zwölf ausgeliefert. Die dreizehnte — automatische Anlage der Ordnerstruktur — ist implementiert, aber im noch nicht zusammengeführten Pull Request. Alle 13 funktionalen Anforderungen sind ausgeliefert, einschließlich der automatischen Anlage der Ordnerstruktur. Bei den nichtfunktionalen Anforderungen sind fünf von sechs erfüllt; nicht erfüllt ist allein die Zusicherung gegen gleichzeitige verändernde Zugriffe. Diese Nebenläufigkeit (Abschnitt~\ref{sec:race-conditions}) ist keine unerfüllte Anforderung, sondern eine neu erkannte Eigenschaft, als eigenes Backlog Item erfasst und freigegeben.
Bei den nichtfunktionalen Anforderungen sind fünf von sechs erfüllt. Die Skalierbarkeit des Lookups ist gelöst, aber nicht ausgeliefert; bis dahin gilt das lineare Verfahren — konzeptionell erfüllt, betrieblich noch nicht wirksam.
Hinzu kommt die Nebenläufigkeit (Abschnitt~\ref{sec:race-conditions}) — keine unerfüllte Anforderung, sondern eine neu erkannte Eigenschaft, als eigenes Backlog Item erfasst und freigegeben.
+6 -27
View File
@@ -1,40 +1,23 @@
\section{Autorisierungskonzept} \section{Autorisierungskonzept}
\label{sec:authorization} \label{sec:authorization}
Der Dokumentenbereich verarbeitet Vertragsunterlagen, Abrechnungsdaten und Security Assessments. Das Autorisierungskonzept arbeitet zweistufig: Eine Rollenprüfung entscheidet, \emph{ob} ein Benutzer den Bereich betreten darf, eine Mandantenprüfung, \emph{welche} Dokumente er sieht. Das Autorisierungskonzept arbeitet zweistufig: Eine Rollenprüfung entscheidet, \emph{ob} ein Benutzer den Bereich betreten darf, eine Mandantenprüfung, \emph{welche} Dokumente er sieht.
\subsection{Authentifizierung und Claims} \subsection{Authentifizierung und Claims}
Die Authentifizierung erfolgt über Microsoft Entra~ID. Das Token enthält zwei für dieses Projekt wesentliche Angaben: Die Authentifizierung erfolgt über Microsoft Entra~ID. Das Token enthält die \textbf{Efecte-Organisations-ID}, über die die Kundenzugehörigkeit bestimmt wird, und den \textbf{Efecte-Firmennamen} aus dem Claim \texttt{efecte:company\_name}. Dass der Firmenname verfügbar ist, wurde erst bei der Lookup-Recherche erkannt — er ermöglicht die Bestimmung des Ordnernamens ohne zusätzlichen Efecte-Aufruf (siehe Abschnitt~\ref{sec:lookup-decision}).
\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. Dass der Firmenname verfügbar ist, wurde erst bei der Recherche zum Kundenordner-Lookup erkannt — er ermöglicht die Bestimmung des Ordnernamens ohne zusätzlichen Efecte-Aufruf (siehe Abschnitt~\ref{sec:lookup-decision}).
\subsection{Anwendungsrolle Documents.Read} \subsection{Anwendungsrolle Documents.Read}
Für den Zugriff auf den Dokumentenbereich wurde in Entra~ID die Anwendungsrolle \texttt{Documents.Read} angelegt. Eine Differenzierung nach Dokumententyp ist nicht vorgesehen: Ein Benutzer sieht entweder alle Dokumente seiner Organisation oder gar keine. Für den Zugriff wurde in Entra~ID die Anwendungsrolle \texttt{Documents.Read} angelegt; eine Differenzierung nach Dokumententyp gibt es nicht. Sie steuert die Sichtbarkeit des Navigationspunkts und schützt die Seite gegen direkten URL-Aufruf. Nur die zweite Prüfung ist sicherheitsrelevant; das Ausblenden des Menüpunkts ist Benutzerführung.
Die Rolle wirkt an zwei Stellen: Sie steuert die Sichtbarkeit des Navigationspunkts und schützt die Seite gegen direkten URL-Aufruf. Die zweite Prüfung ist sicherheitsrelevant; das Ausblenden des Menüpunkts ist reine Benutzerführung.
\subsection{403 statt 404} \subsection{403 statt 404}
Ein diskutierter Punkt war die Antwort bei unberechtigtem Zugriff auf \texttt{/documents}. Die ursprüngliche Anforderung sah 404 vor, um die Existenz der Ressource zu verbergen. Die ursprüngliche Anforderung sah bei unberechtigtem Zugriff auf \texttt{/documents} eine 404-Antwort vor, um die Existenz der Ressource zu verbergen. Sie wurde auf 403 geändert: Die Existenz des Bereichs ist kein Geheimnis, und 404 erschwert die Fehlersuche, weil ein Benutzer ohne Rolle dieselbe Antwort wie bei einem Tippfehler erhält. Dieser Fall trat später im Abnahmetest ein (siehe Abschnitt~\ref{sec:acceptance-testing}) und bestätigte die Entscheidung.
Die Anforderung wurde auf 403 geändert. Die Existenz des Dokumentenbereichs ist kein Geheimnis. Zudem erschwert 404 die Fehlersuche: Ein Benutzer ohne Rolle erhält dieselbe Antwort wie bei einem Tippfehler. Dieser Fall trat später im Abnahmetest ein (siehe Abschnitt~\ref{sec:acceptance-testing}) und bestätigte die Entscheidung.
Der Trade-off: minimaler Informationsgewinn für einen Angreifer gegen deutlich bessere Diagnostizierbarkeit.
\subsection{Mandantentrennung} \subsection{Mandantentrennung}
Die zweite Stufe stellt sicher, dass ein berechtigter Benutzer ausschließlich die Dokumente seiner Organisation sieht. Aus der Organisations-ID wird der zugehörige Kundenordner aufgelöst, und alle Abfragen werden auf dieses Präfix eingeschränkt (siehe Abschnitt~\ref{sec:s3-layout}). Aus der Organisations-ID wird der zugehörige Kundenordner aufgelöst, und alle Abfragen werden auf dieses Präfix eingeschränkt (siehe Abschnitt~\ref{sec:s3-layout}). Da die Einschränkung bereits Bestandteil der Abfrage ist, lädt Houston nie Dokumente fremder Kunden, um sie nachträglich herauszufiltern; ein Fehler in der Darstellungsschicht kann somit nicht zu einer Offenlegung führen. Abbildung~\ref{fig:auth-sequence} stellt den Ablauf dar.
Entscheidend ist, dass die Einschränkung bereits Bestandteil der Abfrage ist — Houston lädt nie Dokumente fremder Kunden, um sie nachträglich 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] \begin{figure}[H]
\centering \centering
@@ -45,8 +28,4 @@ Abbildung~\ref{fig:auth-sequence} stellt den vollständigen Ablauf dar.
\subsection{Absicherung der Einzelzugriffe} \subsection{Absicherung der Einzelzugriffe}
Auch Download, ZIP-Download, PDF-Vorschau und Freigabelinks nehmen jeweils einen Dokumentschlüssel entgegen. Für jeden Pfad wird der angeforderte Schlüssel gegen das aufgelöste Kundenpräfix geprüft, bevor auf den Speicher zugegriffen wird. Auch Download, ZIP-Download, PDF-Vorschau und Freigabelinks nehmen einen Dokumentschlüssel entgegen. Für jeden Pfad wird der Schlüssel gegen das aufgelöste Kundenpräfix geprüft und auf Bestandteile untersucht, mit denen sich der Kundenordner verlassen ließe (NFA-6). Freigabelinks bilden keine Ausnahme: Sie enthalten keine Anmeldeinformationen; ein Empfänger ohne Rolle und passende Organisationszugehörigkeit erhält dieselbe 403-Antwort.
Zusätzlich wird der Pfad auf Bestandteile untersucht, mit denen sich der Kundenordner verlassen ließe (NFA-6). Ohne diese Prüfung könnte ein Benutzer durch Manipulation des Parameters auf fremde Ordner zugreifen.
Freigabelinks stellen keine Ausnahme dar: Sie enthalten keine Anmeldeinformationen und gewähren keinen eigenständigen Zugriff. Ein Empfänger ohne Rolle und passende Organisationszugehörigkeit erhält dieselbe 403-Antwort. Der Link verweist lediglich innerhalb des berechtigten Personenkreises auf ein Dokument.
+4 -18
View File
@@ -26,28 +26,14 @@ Die sieben Dokumententypen wurden in der Feature-Beschreibung festgelegt und im
\subsection{Feste Kodierung statt Konfiguration} \subsection{Feste Kodierung statt Konfiguration}
Im Approval-Termin stellte \emph{Timo Walter} die Frage, ob die Kategorien konfigurierbar sein sollen. Die Entscheidung fiel auf eine feste Kodierung in Houston. Im Approval-Termin stellte \emph{Timo Walter} die Frage, ob die Kategorien konfigurierbar sein sollen. Die Entscheidung fiel auf eine feste Kodierung in Houston. Der Typenkatalog ist fachlich stabil und ändert sich allenfalls im Rhythmus von Jahren; Konfigurierbarkeit würde Verwaltungsoberfläche, Speicherformat und Migrationsstrategie erfordern. Da jeder Typ zudem ein Icon und eine Übersetzung benötigt, stellt die feste Kodierung sicher, dass Ordnername, Anzeigetext und Icon gemeinsam gepflegt werden. Der Preis — eine Codeänderung und ein Release je neuem Typ — ist bei der erwarteten Änderungshäufigkeit vertretbar.
Der Typenkatalog ist fachlich stabil und ändert sich allenfalls im Rhythmus von Jahren. Konfigurierbarkeit würde Verwaltungsoberfläche, Speicherformat und Migrationsstrategie erfordern — ein unverhältnismäßiger Aufwand.
Hinzu kommt, dass jeder Typ ein Icon und eine Übersetzung benötigt, die bei einem frei konfigurierbaren Katalog ebenfalls hinterlegbar sein müssten. Die feste Kodierung stellt sicher, dass Ordnername, Anzeigetext und Icon gemeinsam gepflegt werden.
Der Preis: Das Hinzufügen eines Typs erfordert eine Codeänderung und ein Release. Angesichts der erwarteten Änderungshäufigkeit ist das vertretbar.
\subsection{Verhalten bei unbekannten Ordnern} \subsection{Verhalten bei unbekannten Ordnern}
Aus der festen Kodierung ergibt sich die Frage, wie mit Unterordnern umzugehen ist, die nicht im Katalog stehen — etwa einem in Filestash von Hand angelegten Ordner \texttt{Sonstiges}. Unterordner, die nicht im Katalog stehen — etwa ein in Filestash von Hand angelegtes \texttt{Sonstiges} — werden angezeigt: Die Typableitung liefert keinen Treffer, das Dokument gilt als typlos und erhält den Anzeigetext \emph{Sonstige Dokumente} sowie ein neutrales Standard-Icon. Ausblenden wurde verworfen, weil es stilles Fehlverhalten erzeugen würde. Dieselbe Behandlung greift für Dokumente unmittelbar im Kundenordner. Praktisch bleibt der Fall die Ausnahme, da Houston die Ordnerstruktur selbst anlegt (siehe Abschnitt~\ref{sec:folder-management}) und dabei nur Katalog-Ordner erzeugt.
Dokumente in solchen Ordnern werden angezeigt: Die Typableitung liefert keinen Treffer, das Dokument gilt als typlos und erhält den Anzeigetext \emph{Sonstige Dokumente} sowie ein neutrales Standard-Icon. Ausblenden wurde verworfen, weil es stilles Fehlverhalten erzeugen würde: Ein Dokument wäre unsichtbar, ohne dass der Betreuer dies bemerkt. Dieselbe Behandlung greift für Dokumente, die unmittelbar im Kundenordner liegen. Zu unterscheiden ist dies vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}): Dort entscheidet die Struktur über die Mandantentrennung, während innerhalb eines Kundenordners die Zugehörigkeit bereits geklärt ist.
Praktisch bleibt der Fall die Ausnahme, da Houston die Ordnerstruktur selbst anlegt und pflegt (siehe Abschnitt~\ref{sec:folder-management}) und dabei ausschließlich Ordner des Katalogs erzeugt.
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt.
Zu unterscheiden ist dieser Fall vom Umgang mit unbekannten Ordnern auf der \emph{obersten} Ebene, wo ein fehlendes Marker-Metadatum zum vollständigen Ausschluss führt (siehe Abschnitt~\ref{sec:s3-layout}). Der Unterschied ist beabsichtigt: Auf oberster Ebene entscheidet die Struktur über die Mandantentrennung; innerhalb eines Kundenordners ist die Zugehörigkeit bereits geklärt.
\subsection{Übersetzung der Anzeigenamen} \subsection{Übersetzung der Anzeigenamen}
Die Ordnernamen im Speicher dienen zwei Zielgruppen: Für die Kundenbetreuer in Filestash müssen sie lesbar und eindeutig sein, für den Kunden in Houston sollen sie sich in die Oberfläche einfügen. Die Ordnernamen müssen für die Kundenbetreuer in Filestash lesbar und eindeutig sein, für den Kunden in Houston aber in die Oberfläche passen. Der Ordnername wird daher über eine Ressourcendatei auf einen Anzeigetext abgebildet — das etablierte Lokalisierungsmuster. So lässt sich der Anzeigetext ändern, ohne die Speicherstruktur anzufassen, deren Umbenennung das Umkopieren sämtlicher betroffenen Objekte erfordern würde.
Der Ordnername wird nicht unverändert angezeigt, sondern über eine Ressourcendatei auf einen Anzeigetext abgebildet. Dies entspricht dem in Houston etablierten Lokalisierungsmuster und erlaubt Änderungen am Anzeigetext, ohne die Speicherstruktur anzufassen — eine Umbenennung dort würde das Umkopieren sämtlicher betroffenen Objekte erfordern.
+9 -25
View File
@@ -3,27 +3,17 @@
\subsection{Die zugrunde liegende Idee} \subsection{Die zugrunde liegende Idee}
Alle in Abschnitt~\ref{sec:lookup-research} betrachteten Ansätze hinterlegen die Zuordnung \emph{zusätzlich} irgendwo — in Cache, Token, Efecte oder einer Zuordnungsdatei. Jeder führt eine zweite, konsistent zu haltende Datenhaltung ein. Alle in Abschnitt~\ref{sec:lookup-research} betrachteten Ansätze hinterlegen die Zuordnung \emph{zusätzlich} irgendwo und führen so eine zweite, konsistent zu haltende Datenhaltung ein. Die gewählte Lösung dreht die Fragestellung um: Lässt sich der Ordnername deterministisch aus dem Firmennamen ableiten und steht dieser im Token, bestimmt Houston ihn ohne Nachschlagevorgang — aus der Suche wird ein direkter Zugriff.
Die gewählte Lösung dreht die Fragestellung um: Lässt sich der Ordnername deterministisch aus dem Firmennamen ableiten und steht dieser im Token, kann Houston den Ordnernamen ohne Nachschlagevorgang bestimmen. Aus der Suche wird ein direkter Zugriff. Voraussetzung ist, dass die Ordner so heißen, wie Houston es erwartet. \textbf{Houston wird zur führenden Instanz für die Namensgebung der Kundenordner}; weicht ein Ordner ab, wird er umbenannt. Das ist vertretbar, weil Houston die Ordnerstruktur ohnehin selbst anlegt (FA-13), und die Kundenbetreuer verlieren nichts: Der Ordner heißt weiterhin nach dem Kunden, nur normalisiert.
Voraussetzung ist, dass die Ordner tatsächlich so heißen, wie Houston es erwartet. \textbf{Houston wird zur führenden Instanz für die Namensgebung der Kundenordner.} Weicht ein Ordner ab, wird er umbenannt.
Diese Festlegung ist vertretbar, weil Houston die Ordnerstruktur ohnehin selbst anlegt (FA-13). Die Kundenbetreuer verlieren nichts: Der Ordner heißt weiterhin nach dem Kunden, nur normalisiert.
\subsection{Ableitung des Ordnernamens} \subsection{Ableitung des Ordnernamens}
Der Firmenname aus dem Token kann nicht unverändert als Ordnername dienen — er kann problematische Zeichen, führende Leerzeichen oder beliebige Länge aufweisen. Er wird durch eine Normalisierungsfunktion geführt, im Folgenden \emph{Slug} genannt. Der Firmenname aus dem Token kann problematische Zeichen, führende Leerzeichen oder beliebige Länge aufweisen und wird daher durch eine Normalisierungsfunktion geführt, im Folgenden \emph{Slug} genannt. Sie muss \textbf{deterministisch} und \textbf{lesbar} sein, da der Ordner in Filestash von Menschen bedient wird. Umlaute werden bewusst \emph{nicht} ersetzt: „Müller GmbH" erhält den Ordner \texttt{Müller GmbH}, nicht \texttt{Mueller GmbH}, sonst stünde die Firma in der alphabetischen Liste an unerwarteter Stelle. Die Zulässigkeit solcher Zeichen in Objektschlüsseln wurde anhand der Herstellerdokumentation geprüft \autocite{aws-s3-naming, ibm-s3-naming}.
Die Normalisierung muss \textbf{deterministisch} sein — derselbe Firmenname muss stets denselben Ordnernamen ergeben — und das Ergebnis \textbf{lesbar} halten, weil der Ordner in Filestash von Menschen bedient wird. Umlaute werden bewusst \emph{nicht} ersetzt: „Müller GmbH" erhält den Ordner \texttt{Müller GmbH}, nicht \texttt{Mueller GmbH}, weil der Betreuer die Firma sonst 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}.
\subsection{Umgang mit Namenskollisionen} \subsection{Umgang mit Namenskollisionen}
Firmennamen sind nicht garantiert eindeutig. Efecte lässt zwei Organisationen mit identischem Namen zu, und beide würden auf denselben Ordnernamen treffen. Da eine Verwechslung bedeuten würde, dass ein Kunde fremde Dokumente sieht, muss der Fall abgedeckt sein. Firmennamen sind nicht garantiert eindeutig: Efecte lässt zwei Organisationen mit identischem Namen zu, die auf denselben Ordnernamen träfen — eine Verwechslung würde bedeuten, dass ein Kunde fremde Dokumente sieht. Die erste Organisation, die den Namen beansprucht, erhält ihn; jede weitere erhält einen Ordner mit angehängter Organisations-ID, etwa \texttt{Beispielkunde GmbH (77)}. Ein Zielname wird nur beansprucht, wenn er frei ist oder ausweislich seines Markers der eigenen Organisation gehört; die Zuordnung entscheidet immer das Metadatum, nie der Name.
Die erste Organisation, die den Namen beansprucht, erhält ihn. Jede weitere erhält einen Ordner mit angehängter Organisations-ID, etwa \texttt{Beispielkunde GmbH (77)} — eindeutig, lesbar und alphabetisch neben dem gleichnamigen Ordner.
Sicherheitsregel: Ein Zielname wird nur beansprucht, wenn er frei ist oder ausweislich seines Markers der eigenen Organisation gehört. Ein fremder Kundenordner wird unter keinen Umständen überschrieben. Die Zuordnung entscheidet immer das Metadatum, nie der Name.
\subsection{Der resultierende Ablauf} \subsection{Der resultierende Ablauf}
@@ -37,21 +27,15 @@ Aus diesen Überlegungen ergibt sich ein dreistufiges Verfahren (Abbildung~\ref{
\end{figure} \end{figure}
\begin{enumerate} \begin{enumerate}
\item \textbf{Direktzugriff.} Houston bildet den erwarteten Ordnernamen aus dem Firmennamen und ruft die Metadaten des Markers ab. Stimmt die Organisations-ID, ist die Auflösung mit einem Aufruf abgeschlossen. Dies ist der Regelfall. \item \textbf{Direktzugriff.} Houston bildet den erwarteten Ordnernamen aus dem Firmennamen und ruft die Metadaten des Markers ab. Stimmt die Organisations-ID, ist die Auflösung mit einem Aufruf abgeschlossen — der Regelfall.
\item \textbf{Kollisionsprüfung.} Fehlt der Marker oder trägt er eine fremde ID, wird der Zugriff mit dem um die Organisations-ID ergänzten Namen wiederholt — zwei Aufrufe für den Kollisionsfall. \item \textbf{Kollisionsprüfung.} Fehlt der Marker oder trägt er eine fremde ID, wird der Zugriff mit dem um die Organisations-ID ergänzten Namen wiederholt — zwei Aufrufe.
\item \textbf{Rückfallebene.} Erst dann kommt das lineare Verfahren zum Einsatz. Wird ein Ordner mit passender ID gefunden, wird er auf den kanonischen Namen umbenannt. Wird keiner gefunden, wird die vollständige Struktur angelegt. \item \textbf{Rückfallebene.} Erst dann kommt das lineare Verfahren zum Einsatz. Ein Ordner mit passender ID wird auf den kanonischen Namen umbenannt; wird keiner gefunden, wird die vollständige Struktur angelegt.
\end{enumerate} \end{enumerate}
\subsection{Selbstheilung} \subsection{Selbstheilung}
Die entscheidende Eigenschaft liegt im dritten Schritt: Die Rückfallebene beseitigt nicht nur den Fehlschlag, sondern auch dessen Ursache. Nach der Umbenennung trägt der Ordner den erwarteten Namen, und der nächste Zugriff läuft direkt. Die Rückfallebene beseitigt nicht nur den Fehlschlag, sondern dessen Ursache: Nach der Umbenennung trägt der Ordner den erwarteten Namen, und der nächste Zugriff läuft direkt. Der teure Pfad wird pro Kunde höchstens einmal durchlaufen, Bestandsordner migrieren sich beim ersten Zugriff von selbst. Das Verfahren erfordert keine zusätzliche Datenhaltung; der Zustand liegt vollständig im Speicher.
Der teure Pfad wird pro Kunde höchstens einmal durchlaufen. Bestehende Ordner migrieren sich beim ersten Zugriff von selbst — eine gesonderte Migration entfällt.
Das Verfahren erfordert keine zusätzliche Datenhaltung: keinen Cache, kein Feld in einem Fremdsystem, keine Zuordnungsdatei. Der Zustand liegt vollständig im Speicher, und die Anwendung kann aus jedem Ausgangszustand den Sollzustand herstellen.
\subsection{Bewusst offen gelassener Bereich} \subsection{Bewusst offen gelassener Bereich}
Die Rückfallebene ist nicht nur lesend: Sie benennt Ordner um und legt sie an — im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen läuft, können zwei Anfragen gleichzeitig denselben Ordner betreffen. Die Rückfallebene benennt Ordner um und legt sie an — im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen läuft, können zwei Anfragen gleichzeitig denselben Ordner betreffen. Dieser Umstand wurde als eigenes Backlog Item vom laufenden Pull Request abgegrenzt; Abschnitt~\ref{sec:race-conditions} behandelt die Szenarien und Gegenmaßnahmen.
Dieser Umstand wurde analysiert und als eigenes Backlog Item vom laufenden Pull Request abgegrenzt. Abschnitt~\ref{sec:race-conditions} behandelt die Szenarien und erwogenen Gegenmaßnahmen.
+9 -33
View File
@@ -3,46 +3,22 @@
\subsection{Entstehung} \subsection{Entstehung}
Der Document Explorer war zum Zeitpunkt seines Merges am 31.~Juli 2026 funktionsfähig, enthielt aber eine im Code-Review angesprochene Schwäche. Um den Kundenordner zu einer Organisations-ID zu finden, listete Houston sämtliche Ordner auf oberster Ebene auf, rief für jeden die Metadaten ab und verglich die \texttt{efecte-org-id} mit der des Benutzers. Der Document Explorer war zu seinem Merge am 31.~Juli 2026 funktionsfähig, enthielt aber eine im Code-Review angesprochene Schwäche: Um den Kundenordner zu einer Organisations-ID zu finden, listete Houston sämtliche Ordner auf oberster Ebene auf, rief für jeden die Metadaten ab und verglich die \texttt{efecte-org-id} mit der des Benutzers. Ursache ist, dass S3 keine Suche nach benutzerdefinierten Metadaten bietet: \texttt{ListObjectsV2} liefert Schlüssel, Größe und Änderungszeitpunkt, aber keine Metadaten \autocite{aws-listobjectsv2}, sodass jeder Kandidat einen eigenen Aufruf erfordert.
Die Ursache: S3 bietet keine Suche nach benutzerdefinierten Metadaten. \texttt{ListObjectsV2} liefert Schlüssel, Größe und Änderungszeitpunkt, aber keine benutzerdefinierten Metadaten \autocite{aws-listobjectsv2}. Für jeden Kandidaten ist ein eigener Aufruf nötig. Der Aufwand wächst linear mit der Kundenzahl — bei \emph{jedem} Seitenaufruf, also einige hundert Netzwerkaufrufe bei einigen hundert Kunden, bevor das erste Dokument geladen wird. Das Problem ist nicht akut, aber strukturell. Am 28.~Juli entstand daraus ein eigenes Backlog Item, bewusst als \emph{Recherche}-Item mit dem Akzeptanzkriterium „es ist sich für einen Lösungsansatz entschieden worden", um den Rechercheaufwand als eigenständige Leistung sichtbar zu machen.
Der Aufwand wächst linear mit der Kundenzahl — bei \emph{jedem} Seitenaufruf. Bei einigen hundert Kunden bedeutet das einige hundert Netzwerkaufrufe, bevor das erste Dokument geladen wird. Das Problem ist nicht akut, aber strukturell.
Am 28.~Juli entstand daraus ein eigenes Backlog Item, bewusst als \emph{Recherche}-Item. Das Akzeptanzkriterium lautete nicht „das Problem ist behoben", sondern „es ist sich für einen Lösungsansatz entschieden worden". So wird der Rechercheaufwand als eigenständige Leistung sichtbar.
\subsection{Untersuchte Lösungsansätze} \subsection{Untersuchte Lösungsansätze}
Die sechs im Folgenden dargestellten Ansätze habe ich im Rahmen des Recherche-Items selbst erarbeitet und gegeneinander abgewogen; \emph{Timo Walter} und \emph{Bianco Veigel} dienten als Gesprächspartner für die Rückversicherung und prüften das Ergebnis im Review. Wo Angaben zur Speicherinfrastruktur nötig waren, wurden diese bei den zuständigen Ansprechpartnern eingeholt. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen. Die sechs folgenden Ansätze habe ich im Rahmen des Recherche-Items selbst erarbeitet und abgewogen; \emph{Timo Walter} und \emph{Bianco Veigel} dienten als Gesprächspartner und prüften das Ergebnis im Review. Tabelle~\ref{tab:lookup-tradeoffs} im Anhang fasst die Bewertung zusammen.
\subsubsection{In-Memory-Cache} \textbf{In-Memory-Cache.} Das Auflösungsergebnis zwischenspeichern macht Folgeaufrufe kostenlos. Da Houston in mehreren Instanzen läuft und der Cache nach jedem Neustart leer ist, bleibt der lineare Aufwand jedoch bestehen, er wird nur seltener bezahlt.
Der naheliegendste Ansatz: das Auflösungsergebnis im Arbeitsspeicher zwischenspeichern. Der erste Aufruf bleibt teuer, alle folgenden sind kostenlos. \textbf{Speicherung im Anmeldetoken.} Der Ordnername könnte als Claim abgelegt werden. Der lineare Aufwand bliebe erhalten, und ein Token ist über seine Laufzeit unveränderlich: Nach einer Umbenennung zeigt der Claim bis zur Erneuerung auf einen nicht mehr existierenden Ordner.
Houston läuft jedoch in mehreren Instanzen, sodass jede ihren Cache aufbauen müsste. Nach jedem Neustart ist der Cache leer, und es entsteht ein Ansturm teurer Auflösungen. Der lineare Aufwand bleibt unverändert — er wird lediglich seltener bezahlt. \textbf{Speicherung als Feld in Efecte.} Der Ordnername könnte an der Organisation in Efecte gepflegt werden. Das löst das Problem technisch, führt aber eine zweite Datenhaltung neben dem Marker-Metadatum ein, die auseinanderlaufen kann, und müsste für jeden Bestandskunden nachgepflegt werden.
\subsubsection{Speicherung im Anmeldetoken} \textbf{S3 Select.} \texttt{SelectObjectContent} filtert den \emph{Inhalt} eines Objekts serverseitig per SQL-ähnlicher Abfrage \autocite{aws-s3-select}; denkbar wäre eine Zuordnungstabelle als Datei. Die Freischaltung wurde am 7.~August bestätigt (siehe Abschnitt~\ref{sec:infrastructure}), doch S3 Select filtert Inhalte einzelner Objekte, nicht Metadaten mehrerer — die Zuordnungsdatei hätte dasselbe Konsistenzproblem wie das Efecte-Feld ohne dessen Werkzeugunterstützung.
Der Ordnername könnte bei der Anmeldung ermittelt und als Claim im Token abgelegt werden. Der lineare Aufwand bliebe erhalten. Hinzu kommt: Ein Token ist über seine Laufzeit unveränderlich. Wird der Ordner umbenannt, zeigt der Claim auf einen nicht mehr existierenden Ordner, bis das Token erneuert wird. \textbf{StorageGRID Search Integration.} Der Search Integration Service spiegelt Objektmetadaten in einen Elasticsearch-Index und ermöglicht eine echte Metadatensuche \autocite{storagegrid-search-integration} — fachlich die sauberste Lösung. Die Anfrage ergab am 17.~August, dass die Funktion derzeit nicht angeboten wird; der Ansatz wurde in den Ausblick übernommen (siehe Abschnitt~\ref{sec:outlook}).
\subsubsection{Speicherung als Feld in Efecte} \textbf{Organisations-ID im Ordnernamen.} Die ID zum Bestandteil des Ordnernamens machen — etwa \texttt{42\_Beispielkunde GmbH} — reduzierte den Lookup auf eine Präfixabfrage. In der Abstimmung mit \emph{Ralf Schulte} wurde deutlich, dass die Kundenbetreuer die Ordner in Filestash nach Kundennamen sortiert vorfinden müssen; ein vorangestellter Zahlenschlüssel zerstört diese Sortierung. Die entscheidende Beobachtung war, dass die Anforderung eine Sortierung nach Kundennamen verlangt — nicht, dass der Ordnername \emph{ausschließlich} aus dem Kundennamen besteht. Das eröffnete den Weg zur in Abschnitt~\ref{sec:lookup-decision} beschriebenen Lösung.
Der Ordnername könnte als Feld an der Organisation in Efecte gepflegt werden. Das löst das Problem technisch, führt aber eine Abhängigkeit ein: Die Zuordnung läge 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}
\texttt{SelectObjectContent} filtert den \emph{Inhalt} eines Objekts serverseitig per SQL-ähnlicher Abfrage \autocite{aws-s3-select}. Denkbar wäre eine Zuordnungstabelle als Datei im Bucket, aus der der passende Eintrag gefiltert wird.
Die Freischaltung wurde beantragt und am 7.~August bestätigt (siehe Abschnitt~\ref{sec:infrastructure}). S3 Select filtert jedoch Inhalte einzelner Objekte, nicht Metadaten mehrerer Objekte. Eine Zuordnungsdatei wäre eine zusätzlich 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 eine echte Metadatensuche \autocite{storagegrid-search-integration}. Fachlich die sauberste Lösung, da sie das Problem an der Wurzel behebt, ohne eine zweite Datenhaltung einzuführen. Die Anfrage ergab am 17.~August, dass die Funktion derzeit nicht angeboten wird; der Ansatz wurde in den Ausblick übernommen (siehe Abschnitt~\ref{sec:outlook}).
\subsubsection{Organisations-ID im Ordnernamen}
Der einfachste Ansatz: die Organisations-ID zum Bestandteil des Ordnernamens machen — etwa \texttt{42\_Beispielkunde GmbH}. Der Lookup reduzierte sich auf eine Präfixabfrage.
In der Abstimmung mit \emph{Ralf Schulte} wurde deutlich, dass die Kundenbetreuer die Ordner in Filestash nach Kundennamen sortiert vorfinden müssen. Ein vorangestellter Zahlenschlüssel zerstört diese Sortierung. Der Konflikt: Houston autorisiert über die Organisations-ID, die Betreuer arbeiten über den Kundennamen, und der Speicher vermittelt nicht zwischen beiden.
Die entscheidende Beobachtung war, 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 zur in Abschnitt~\ref{sec:lookup-decision} beschriebenen Lösung.
+9 -23
View File
@@ -1,19 +1,17 @@
\section{Ablagekonzept im S3-Speicher} \section{Ablagekonzept im S3-Speicher}
\label{sec:s3-layout} \label{sec:s3-layout}
Das Ablagekonzept muss zwei Aufgaben erfüllen: die Zuordnung von Dokumenten zu Kunden und die Abbildung des fachlichen Dokumententyps. Beides geschieht ausschließlich über die Struktur im Speicher, ohne eine zusätzliche Datenbank. Das Ablagekonzept muss die Zuordnung von Dokumenten zu Kunden und die Abbildung des fachlichen Dokumententyps leisten — beides ausschließlich über die Struktur im Speicher, ohne zusätzliche Datenbank.
\subsection{Präfixe statt Ordner} \subsection{Präfixe statt Ordner}
S3 kennt technisch keine Ordner. Jedes Objekt wird über einen Schlüssel in einem flachen Namensraum adressiert. Die scheinbare Hierarchie entsteht beim Auflisten: \texttt{ListObjectsV2} liefert mit Präfix und Trennzeichen nur die Objekte unterhalb eines Präfixes sowie die gemeinsamen Teilpräfixe der nächsten Ebene \autocite{aws-listobjectsv2}. Ein Schlüssel wie S3 adressiert jedes Objekt über einen Schlüssel in einem flachen Namensraum; die scheinbare Hierarchie entsteht beim Auflisten über Präfix und Trennzeichen \autocite{aws-listobjectsv2}. Ein Schlüssel wie
\begin{quote} \begin{quote}
\texttt{Beispielkunde GmbH/Service-Protokoll/Protokoll-2026-07.pdf} \texttt{Beispielkunde GmbH/Service-Protokoll/Protokoll-2026-07.pdf}
\end{quote} \end{quote}
erscheint in Filestash als zweistufige Ordnerhierarchie, ist im Speicher aber nur eine Zeichenkette. Ein „Ordner" existiert erst, wenn mindestens ein Objekt mit dem entsprechenden Präfix vorhanden ist; ein leerer Ordner lässt sich nur durch ein Platzhalterobjekt simulieren, dessen Schlüssel auf das Trennzeichen endet. erscheint in Filestash als zweistufige Ordnerhierarchie, ist im Speicher aber nur eine Zeichenkette. Ein „Ordner" existiert erst mit einem Objekt unter dem Präfix; ein leerer Ordner lässt sich nur durch ein Platzhalterobjekt simulieren, dessen Schlüssel auf das Trennzeichen endet. Abbildung~\ref{fig:s3-layout} zeigt die Struktur.
Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -24,36 +22,24 @@ Abbildung~\ref{fig:s3-layout} zeigt die resultierende Struktur.
\subsection{Kundenzuordnung über ein Marker-Objekt} \subsection{Kundenzuordnung über ein Marker-Objekt}
Auf der obersten Ebene des Buckets liegt für jeden Kunden ein Ordner. Dessen Platzhalterobjekt trägt ein benutzerdefiniertes Metadatum \texttt{efecte-org-id} zur Zuordnung an eine Organisation und wird im Folgenden als \emph{Marker} bezeichnet. Auf der obersten Ebene liegt für jeden Kunden ein Ordner. Dessen Platzhalterobjekt trägt ein benutzerdefiniertes Metadatum \texttt{efecte-org-id} und wird im Folgenden \emph{Marker} genannt. Er sorgt dafür, dass der Kundenordner auch ohne Dokumente existiert, und trägt die Organisationszugehörigkeit an genau einer Stelle.
Er sorgt dafür, dass der Kundenordner auch ohne Dokumente existiert, und trägt die Organisationszugehörigkeit an genau einer Stelle. Die Organisations-ID wird als Metadatum statt als Bestandteil des Ordnernamens geführt (siehe Abschnitt~\ref{sec:feature-analysis}): Die Ordner werden von Kundenbetreuern über Filestash gepflegt, ein Name wie \texttt{42} wäre dort schwer handhabbar. Genau diese Entscheidung erzeugt das zentrale technische Problem des Projekts (Abschnitt~\ref{sec:lookup-research}): Houston kennt die Organisations-ID, muss daraus den Ordnernamen ermitteln — und S3 bietet keine Suche nach Metadaten.
Die Organisations-ID wird als Metadatum statt als Bestandteil des Ordnernamens geführt (siehe Abschnitt~\ref{sec:feature-analysis}). Die Ordner werden von Kundenbetreuern über Filestash gepflegt; ein Name wie \texttt{42} wäre dort schwer handhabbar. Der lesbare Firmenname bleibt daher der Ordnername, die technische Zuordnung wandert in die Metadaten.
Genau diese Entscheidung erzeugt das zentrale technische Problem des Projekts (Abschnitt~\ref{sec:lookup-research}): Houston kennt die Organisations-ID, muss daraus den Ordnernamen ermitteln — und S3 bietet keine Suche nach Metadaten.
\subsection{Typisierung über Unterordner} \subsection{Typisierung über Unterordner}
Innerhalb des Kundenordners liegt für jeden der sieben Dokumententypen ein Unterordner. Der Typ ergibt sich aus dem Unterordner; direkt im Kundenordner liegende Dokumente gelten als untypisiert. Innerhalb des Kundenordners liegt für jeden der sieben Dokumententypen ein Unterordner; der Typ ergibt sich aus dem Unterordner, direkt im Kundenordner liegende Dokumente gelten als untypisiert. Die Alternative — den Typ als Metadatum an jeder Datei zu hinterlegen — wurde verworfen: Ein Metadatum müsste bei jedem Upload manuell gesetzt werden, wofür Filestash keine komfortable Unterstützung bietet, während das Ablegen in einem Ordner die natürliche Bedienhandlung ist. Zudem ist der Typ als Präfixbestandteil unmittelbar aus dem Schlüssel ablesbar und serverseitig filterbar, während ein Metadatum für jedes Objekt einzeln abgerufen werden müsste, da \texttt{ListObjectsV2} es nicht mitliefert — bei $n$ Dokumenten also $n$ zusätzliche Aufrufe.
Die Alternative — den Typ als Metadatum an jeder Datei zu hinterlegen — wurde verglichen: Dem stehen zwei bewusst in Kauf genommene Nachteile gegenüber: Ein Dokument kann nur einen Typ haben, und die Ordner müssen existieren — die automatische Anlage wird damit zu einer eigenen Anforderung (FA-13).
\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 die natürliche Bedienhandlung.
\item \textbf{Abfragbarkeit:} Der Typ ist als Präfixbestandteil unmittelbar aus dem Schlüssel ablesbar. Ein Metadatum müsste dagegen für jedes Objekt einzeln abgerufen werden, da \texttt{ListObjectsV2} benutzerdefinierte Metadaten nicht mitliefert — bei $n$ Dokumenten also $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: Ein Dokument kann nur einen Typ haben, und die Ordner müssen existieren — die automatische Anlage wird damit zu einer eigenen Anforderung (FA-13). Beide Einschränkungen wurden bewusst in Kauf genommen.
\subsection{Sichtbarkeitsregeln} \subsection{Sichtbarkeitsregeln}
Für die Darstellung gelten drei Regeln: Für die Darstellung gelten drei Regeln:
\begin{enumerate} \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. Der Benutzer sieht eine flache Liste aller seiner Dokumente. \item \textbf{Ordner werden nicht als Einträge angezeigt.} Die Liste zeigt ausschließlich Dokumente; die Ordnerstruktur wird über Typ-Icons und Filter abgebildet.
\item \textbf{Leere Typordner erscheinen nicht.} \item \textbf{Leere Typordner erscheinen nicht.}
\item \textbf{Ordner ohne gültiges Marker-Metadatum werden ignoriert.} Fehlt die Organisations-ID, bleibt der Ordner unsichtbar. Der Fall wird protokolliert, führt aber nicht zu einem Fehler. \item \textbf{Ordner ohne gültiges Marker-Metadatum werden ignoriert.} Fehlt die Organisations-ID, bleibt der Ordner unsichtbar; der Fall wird protokolliert, führt aber nicht zu einem Fehler.
\end{enumerate} \end{enumerate}
Die dritte Regel ist eine Sicherheitsmaßnahme: Ein fehlendes Metadatum führt zum Ausschluss, nicht zu einer Vermutung. Die dritte Regel ist eine Sicherheitsmaßnahme: Ein fehlendes Metadatum führt zum Ausschluss, nicht zu einer Vermutung.
+7 -15
View File
@@ -3,19 +3,13 @@
\subsection{Abstimmung der Typfilterung über einen Clickdummy} \subsection{Abstimmung der Typfilterung über einen Clickdummy}
Das Oberflächenkonzept des Dokumentenbereichs stammt bis auf einen Punkt aus der eigenen Ausarbeitung: Für die Typfilterung existierte kein Vorbild in Houston, und da \emph{Hanna Ebner} die führende Entscheidungsinstanz für die Oberfläche der Anwendung ist, wurde das Aussehen dieser Filterleiste mit ihr abgestimmt. Als Referenz setzte sie ihren Vorschlag am 22.~Juli 2026 als lauffähigen Clickdummy in einem eigenen Branch des Houston-Repositories um und verlinkte ihn am Feature. Das Oberflächenkonzept stammt bis auf einen Punkt aus der eigenen Ausarbeitung: Für die Typfilterung existierte kein Vorbild in Houston, und da \emph{Hanna Ebner} die führende Entscheidungsinstanz für die Oberfläche ist, wurde die Filterleiste mit ihr abgestimmt. Als Referenz setzte sie ihren Vorschlag am 22.~Juli 2026 als lauffähigen Clickdummy in einem eigenen Branch des Houston-Repositories um und verlinkte ihn am Feature. Er ist eine gestalterische Vorgabe für ein einzelnes Bedienelement, keine Zuarbeit zur Umsetzung: Die Implementierung der Filterleiste — Markup, Zustandshaltung, Abfrageparameter und serverseitige Auswertung (Abschnitt~\ref{sec:filter-pagination}) — lag wie die des übrigen Dokumentenbereichs vollständig in meiner Hand.
Der Clickdummy verwendet die bestehenden Komponenten und Stile der Anwendung, wodurch Fragen zu Abständen, Schriftgrößen oder Farben durch das vorhandene Stylesheet beantwortet werden. Interaktionen wie das Ein- und Ausklappen von Filtern lassen sich ausprobieren statt aus einer statischen Abbildung erschlossen werden.
Er ist damit eine gestalterische Vorgabe für ein einzelnes Bedienelement, keine Zuarbeit zur Umsetzung: Die Implementierung der Filterleiste — Markup, Zustandshaltung, Abfrageparameter und serverseitige Auswertung (Abschnitt~\ref{sec:filter-pagination}) — lag wie die des übrigen Dokumentenbereichs vollständig in meiner Hand.
\subsection{Flache Liste statt navigierbarer Hierarchie} \subsection{Flache Liste statt navigierbarer Hierarchie}
Der Dokumentenbereich stellt trotz seiner Bezeichnung als „Document Explorer" keine navigierbare Ordnerhierarchie dar. Der Benutzer sieht eine flache Liste aller Dokumente; die Typzugehörigkeit wird über Icons und Filter ausgedrückt. Der Dokumentenbereich stellt trotz der Bezeichnung „Document Explorer" keine navigierbare Ordnerhierarchie dar; der Benutzer sieht eine flache Liste aller Dokumente, die Typzugehörigkeit wird über Icons und Filter ausgedrückt. Bei einer Hierarchie müsste ein Kunde die Kategorie kennen und sich dorthin durchklicken, während die flache Liste unmittelbares Suchen und Filtern erlaubt. Bei nur zwei Ebenen mit sieben festen Kategorien stünde der Navigationsaufwand in keinem Verhältnis zum Nutzen.
Ein Kunde sucht in der Regel ein bestimmtes Dokument. Bei einer Hierarchie müsste er zunächst die Kategorie kennen und sich dorthin durchklicken. Die flache Liste erlaubt unmittelbares Suchen und Filtern. Da die Hierarchie nur zwei Ebenen mit sieben festen Kategorien umfasst, stünde der Navigationsaufwand in keinem Verhältnis zum Nutzen. Die ursprüngliche Feature-Beschreibung sah Dokumentkacheln vor. Die Darstellung wurde auf Zeilen umgestellt — eine Entscheidung von \emph{Hanna Ebner}, die meine abweichende Einschätzung überwog und sich als tragfähiger erwies: Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen, fügt sich in die Tabellendarstellung der übrigen Houston-Seiten ein (NFA-4) und lässt sich um Spalten erweitern, ohne den Aufbau zu verändern.
Die ursprüngliche Feature-Beschreibung sah Dokumentkacheln vor. Die Darstellung wurde auf Zeilen umgestellt — eine Entscheidung von \emph{Hanna Ebner}, die als Entscheidungsinstanz für die Oberfläche meine abweichende Einschätzung überwog. Sie ist im Ergebnis die tragfähigere: Eine Zeile bietet Platz für Icon, Name, Auswahlbox und Aktionsschaltflächen, fügt sich in die Tabellendarstellung der übrigen Houston-Seiten ein (NFA-4) und lässt sich um Spalten erweitern, ohne den Aufbau zu verändern.
\subsection{Aufbau der Seite} \subsection{Aufbau der Seite}
@@ -23,8 +17,8 @@ Die Seite gliedert sich von oben nach unten in vier Bereiche (Abbildung~\ref{fig
\begin{enumerate} \begin{enumerate}
\item Eine \textbf{Suchleiste} am oberen Rand, über die nach dem Dokumentnamen gesucht wird. \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 Darunter \textbf{Filterelemente}, 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 Die \textbf{Dokumentenliste} als Tabelle. Jede Zeile enthält Typ-Icon, 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 Seitenwechsel sowie die Auswahl der Seitengröße. \item Am unteren Rand die \textbf{Blätterelemente} zum Seitenwechsel sowie die Auswahl der Seitengröße.
\end{enumerate} \end{enumerate}
@@ -33,8 +27,6 @@ Suche und Typfilter wirken zusammen und schränken die Liste gemeinsam ein
\subsection{Konsistenz zur bestehenden Anwendung} \subsection{Konsistenz zur bestehenden Anwendung}
Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen Houston-Seiten bedienen lässt (NFA-4). Eine durchgängige Vorgabe war, dass sich der Dokumentenbereich wie die übrigen Houston-Seiten bedienen lässt (NFA-4). Wie genau das zu verstehen ist, zeigte sich erst im Abnahmetest: Die Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren ein — eine Funktion, die es auf den übrigen Seiten nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}) und verdeutlicht, dass eine Konsistenzanforderung am Vergleich mit dem Bestand geprüft werden muss.
Wie genau diese Vorgabe zu verstehen ist, zeigte sich erst im Abnahmetest: Die Suchleiste blendete nach einer Eingabe eine Schaltfläche zum Leeren ein — eine sinnvolle Funktion, die es auf den übrigen Seiten nicht gibt. Der Unterschied wurde als Fehler gemeldet (siehe Abschnitt~\ref{sec:acceptance-testing}). Das Beispiel verdeutlicht, dass eine Konsistenzanforderung am Vergleich mit dem Bestand geprüft werden muss. Ein Freigabelink auf ein bestimmtes Dokument muss auch funktionieren, wenn dieses nicht auf der ersten Seite liegt. Der Link setzt daher beim Weiterleiten die passenden Abfrageparameter, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird (Abbildung~\ref{fig:shot-share-target} im Anhang).
Ein Freigabelink, der auf ein bestimmtes Dokument verweist, muss auch funktionieren, wenn dieses nicht auf der ersten Seite liegt. Der Link setzt daher beim Weiterleiten die passenden Abfrageparameter, sodass die richtige Seite geladen und an die entsprechende Stelle gesprungen wird (Abbildung~\ref{fig:shot-share-target} im Anhang).
+6 -22
View File
@@ -3,7 +3,7 @@
\subsection{Schichtung} \subsection{Schichtung}
Das Dokumentenmodul folgt der in Houston etablierten Schichtung. Abbildung~\ref{fig:module-components} zeigt die Komponenten. Das Dokumentenmodul folgt der in Houston etablierten Schichtung (Abbildung~\ref{fig:module-components}).
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -12,35 +12,19 @@ Das Dokumentenmodul folgt der in Houston etablierten Schichtung. Abbildung~\ref{
\label{fig:module-components} \label{fig:module-components}
\end{figure} \end{figure}
Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Suche, Filter und Blätterelemente, \texttt{Document/Download} für Downloadanforderungen (einzeln und als ZIP) und \texttt{Document/Share} für Freigabelinks. Die Aufteilung folgt daraus, dass Download und Freigabe eigene Routen mit eigenen Antwortformaten benötigen. Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Suche, Filter und Blätterelemente, \texttt{Document/Download} für Downloads (einzeln und als ZIP) und \texttt{Document/Share} für Freigabelinks. Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts (Kundenordner-Auflösung, Typableitung, Ausblendung technischer Einträge). Der \texttt{S3DocumentsClient} kapselt den Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts: Auflösung des Kundenordners, Typableitung aus dem Pfad, Ausblendung technischer Einträge. Der \texttt{S3DocumentsClient} kapselt den technischen Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
\subsection{Eigene Typen statt Zeichenketten} \subsection{Eigene Typen statt Zeichenketten}
\label{sec:newtype} \label{sec:newtype}
Eine Entwurfsentscheidung, die sich im Verlauf herausbildete, betrifft den Umgang mit Pfaden. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde. Als Ausweg verpackte ich die Pfade nach dem \emph{Newtype-Pattern} in eigene \texttt{readonly record struct}-Typen \autocite{rust-newtype}, ergänzt um „parse, don’t validate“ \autocite{king-parse}: Wer einen \texttt{DocumentKey} entgegennimmt, muss die Gültigkeit nicht erneut prüfen.
Der Ausweg war ein Muster, das ich außerhalb der Arbeit beim Programmieren in Rust kennengelernt habe: das \emph{Newtype-Pattern}. Ein primitiver Wert wird in einen eigenen, sonst inhaltsgleichen Typ verpackt, damit der Übersetzer zwei Werte unterscheiden kann, die als Zeichenkette identisch aussehen \autocite{rust-newtype}. In C\# lässt sich das mit \texttt{readonly record struct} ohne Laufzeitkosten nachbilden. Gerade hier lag der Nutzen auf der Hand, weil das Modul fast ausschließlich mit Schlüsseln arbeitet, die zerlegt, umgeformt und wieder zusammengesetzt werden. So entstanden \texttt{DocumentKey} für den vollständigen Pfad einschließlich Kundenordner (Listing~\ref{lst:document-key}) und \texttt{DocumentRelativePath} für den Anteil darunter, beide nur über eine prüfende Fabrikmethode erzeugbar; eine vergessene Prüfung führt so zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName}, \texttt{DocumentId} und \texttt{OrgSlug}. Zeichenketten werden einmal am Rand in diese Typen geparst; die weitere Verarbeitung arbeitet nur noch auf Typen. Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring, das sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
Ergänzt wird das Muster durch die Haltung „parse, don’t validate“ \autocite{king-parse}: Eine Prüfung soll nicht nur ein Ja oder Nein zurückgeben, sondern das geprüfte Ergebnis in einem Typ festhalten, der die Zusicherung trägt. Eine Funktion, die einen \texttt{DocumentKey} entgegennimmt, muss die Gültigkeit nicht erneut prüfen — sie wäre sonst gar nicht aufrufbar gewesen.
So entstanden \texttt{DocumentKey} für den vollständigen Pfad einschließlich Kundenordner (Listing~\ref{lst:document-key}) und \texttt{DocumentRelativePath} für den Anteil darunter. Beide sind nur über eine Fabrikmethode erzeugbar, die zerlegt und dabei prüft; eine vergessene Prüfung führt zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName}, \texttt{DocumentId} und \texttt{OrgSlug}.
Der Datenfluss folgt daraus unmittelbar: Aus der Anfrage kommt eine Zeichenkette, der Speicher liefert Schlüssel als Zeichenketten zurück, beide werden einmal am Rand in das Domänenmodell geparst. Die gesamte weitere Verarbeitung — Typableitung, Filterung, Sortierung, Blätterung — arbeitet nur noch auf Typen. Erst wenn ein weiterer Speicheraufruf nötig ist, wird aus dem Modell wieder ein Schlüssel erzeugt. Zeichenketten existieren damit ausschließlich an den Systemgrenzen.
Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring — ein Aufwand, der sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
\subsection{Zentrale Autorisierung} \subsection{Zentrale Autorisierung}
Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration erfolgt und eine zusätzliche Prüfung an der Seite überflüssig ist. Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration erfolgt und eine zusätzliche Prüfung an der Seite überflüssig ist. Eine seitenspezifische Prüfung ist zudem leicht zu übersehen, wenn weitere Seiten hinzukommen — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt sicher, dass alle Routen derselben Richtlinie unterliegen.
Der Einwand betrifft mehr als doppelten Code: Eine seitenspezifische Prüfung ist leicht zu übersehen, wenn weitere Seiten hinzukommen — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt sicher, dass alle Routen des Moduls derselben Richtlinie unterliegen.
\subsection{Gestapelte Pull Requests} \subsection{Gestapelte Pull Requests}
Ein Vorgehen, das sich durch die gesamte Umsetzung zieht, ist die Verkettung der Pull Requests. Der erste ging gegen den Hauptbranch; jeder darauf aufbauende gegen den Branch seines Vorgängers. Die Pull Requests wurden verkettet: Der erste ging gegen den Hauptbranch, jeder darauf aufbauende gegen den Branch seines Vorgängers. So enthält jeder Pull Request genau die Änderungen seines Backlog Items, was die Reviewbarkeit erhöht. Der Preis zeigte sich beim Zusammenführen: Sobald ein Vorgänger übernommen war, musste der Nachfolger umgestellt werden, was in Azure DevOps die bereits abgegebenen Freigaben zurücksetzt. Bei der vorliegenden Zahl aufeinander aufbauender Items überwog der Gewinn an Reviewbarkeit diesen Aufwand deutlich.
Der Grund ist die Reviewbarkeit. Da die Arbeiten aufeinander aufbauen, hätte ein direkter Vergleich gegen den Hauptbranch auch sämtliche Vorgängeränderungen enthalten. Durch die Verkettung enthält jeder Pull Request genau die Änderungen seines Backlog Items.
Der Preis zeigte sich beim Zusammenführen: Sobald ein Vorgänger in den Hauptbranch übernommen war, musste der Nachfolger umgestellt werden. Diese Umstellung setzt in Azure DevOps die bereits abgegebenen Freigaben zurück. Für künftige Arbeiten wäre abzuwägen, ob der Gewinn an Reviewbarkeit diesen Abstimmungsaufwand rechtfertigt; bei der vorliegenden Zahl aufeinander aufbauender Items überwog er deutlich.
+5 -17
View File
@@ -1,30 +1,18 @@
\section{Document Explorer} \section{Document Explorer}
\label{sec:document-explorer} \label{sec:document-explorer}
\subsection{Umfang} \subsection{Umfang und Darstellung}
Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten das umfangreichste. Er umfasst die Grundstruktur des gesamten Moduls: Seite, Dienst, Speicherclient, Modelle sowie Einbindung in Navigation und Autorisierung. Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten das umfangreichste. Er umfasst die Grundstruktur des Moduls: Seite, Dienst, Speicherclient, Modelle sowie Einbindung in Navigation und Autorisierung. Die ursprüngliche Kacheldarstellung wurde auf Zeilendarstellung geändert, da Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschau im Backlog standen, für die eine Kachel keinen natürlichen Platz geboten hätte; die Tabelle ist zudem um weitere Spalten erweiterbar.
\subsection{Vom Kachel- zum Listenentwurf}
Die ursprüngliche Fassung sah eine Kacheldarstellung vor. Im Verlauf wurde auf Zeilendarstellung geändert, da Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschau im Backlog standen. Eine Kachel hätte für diese Elemente keinen natürlichen Platz geboten. Die Tabelle soll flexibel um weitere Spalten erweiterbar sein.
\subsection{Ableitung des Dokumententyps} \subsection{Ableitung des Dokumententyps}
Der Typ eines Dokuments ergibt sich aus dem Unterordnernamen und wird gegen eine Aufzählung abgeglichen. Im Review schlug \emph{Timo Walter} vor, die Zeichenketten-Umwandlung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden statt einer eigenen Zuordnung. Beim Hinzufügen eines Typs muss so nur die Aufzählung ergänzt werden. Schlägt die Umwandlung fehl, gilt das Dokument als untypisiert. Der Typ eines Dokuments ergibt sich aus dem Unterordnernamen und wird gegen eine Aufzählung abgeglichen. Im Review schlug \emph{Timo Walter} vor, die Zeichenketten-Umwandlung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden statt einer eigenen Zuordnung; beim Hinzufügen eines Typs muss so nur die Aufzählung ergänzt werden. Schlägt die Umwandlung fehl, gilt das Dokument als untypisiert. Eine bewusste Ausnahme betrifft die Übersetzung: Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird eine Ausnahme ausgelöst statt still auf den Ordnernamen zurückzufallen — eine fehlende Übersetzung ist eine unvollständige Implementierung.
Eine bewusste Ausnahme betrifft die Übersetzung: Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird eine Ausnahme ausgelöst. Jeder Wert der Aufzählung ist per Definition gültig; fehlt seine Übersetzung, ist das eine unvollständige Implementierung. Ein stiller Rückfall auf den Ordnernamen würde diesen Fehler verdecken.
\subsection{Leerer Zustand und Fehlerbehandlung} \subsection{Leerer Zustand und Fehlerbehandlung}
Zwei Anforderungen betreffen Situationen ohne anzeigbare Dokumente und sind zu unterscheiden. Zwei Situationen ohne anzeigbare Dokumente sind zu unterscheiden. Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber keine Dokumente vorliegen — insbesondere unmittelbar nach der automatischen Ordneranlage; er wird mit einem Hinweis dargestellt. Ein \textbf{Fehler} liegt vor, wenn die Dokumente nicht geladen werden konnten; im Review wurde angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf.
Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber keine Dokumente vorliegen — insbesondere unmittelbar nach der automatischen Ordneranlage. Er wird mit einem Hinweis dargestellt.
Ein \textbf{Fehler} liegt vor, wenn die Dokumente nicht geladen werden konnten. Im Review wurde angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf: Im einen Fall gibt es nichts zu sehen, im anderen funktioniert die Anwendung nicht.
\subsection{Die bekannte Schwäche} \subsection{Die bekannte Schwäche}
Bereits im Review fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage nötig sei. Die Antwort war, dass die Schnittstelle dies nicht anders zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item. Bereits im Review fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage nötig sei. Die Antwort war, dass die Schnittstelle dies nicht anders zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item. Die Schwäche wurde bewusst zurückgestellt; die tatsächliche Lösung beschreibt Abschnitt~\ref{sec:folder-management}.
Die Schwäche wurde erkannt und bewusst zurückgestellt — der Explorer sollte nicht daran scheitern, dass eine Optimierung noch nicht gefunden war. Die tatsächliche Lösung beschreibt Abschnitt~\ref{sec:folder-management}.
+4 -10
View File
@@ -3,17 +3,11 @@
\subsection{Einzeldownload über zeitlich begrenzte Zugriffs-URLs} \subsection{Einzeldownload über zeitlich begrenzte Zugriffs-URLs}
Für den Einzeldownload wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}. Die Datenübertragung läuft nicht über die Anwendung; ein großes Dokument belegt weder Arbeitsspeicher noch Verbindungen des Webservers. Die URL ist nur wenige Minuten gültig. Für den Einzeldownload wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}. Die Datenübertragung läuft nicht über die Anwendung, und die URL ist nur wenige Minuten gültig. Die Berechtigungsprüfung findet \emph{vor} dem Erzeugen der URL statt, da die URL selbst keine Prüfung trägt: Sie darf nur für einen Schlüssel erzeugt werden, der nachweislich im Kundenordner liegt. Genau hier setzte der überwiegende Teil des Reviews an und führte zur in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen. Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt.
Wichtig ist, dass die Berechtigungsprüfung \emph{vor} dem Erzeugen der URL stattfindet. Die URL selbst trägt keine Prüfung — wer sie besitzt, kann zugreifen. Sie darf deshalb nur für einen Schlüssel erzeugt werden, der nachweislich im Kundenordner liegt. Genau hier setzte der überwiegende Teil des Reviews an, und die Rückfragen führten zur in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen.
Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt, da der Schlüssel den vollständigen Pfad enthält.
\subsection{ZIP-Download} \subsection{ZIP-Download}
Jede Zeile erhält eine Auswahlbox für den ZIP-Download. Die Schaltfläche ist deaktiviert, solange nichts ausgewählt ist. Jede Zeile erhält eine Auswahlbox für den ZIP-Download; die Schaltfläche ist deaktiviert, solange nichts ausgewählt ist. Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -22,8 +16,8 @@ Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
\label{fig:zip-stream} \label{fig:zip-stream}
\end{figure} \end{figure}
Das Archiv wird erst beim Klick erzeugt. Entscheidend ist, dass es nie vollständig im Arbeitsspeicher entsteht: Die Anwendung öffnet die Antwort als Archivstrom, liest die Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Da die Gesamtgröße unbegrenzt ist, würde ein vollständiger Aufbau den Speicherbedarf von der größten Auswahl abhängig machen. Beim Strömen bleibt er konstant. Das Archiv wird erst beim Klick erzeugt und entsteht nie vollständig im Arbeitsspeicher: Die Anwendung öffnet die Antwort als Archivstrom, liest die Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Da die Gesamtgröße unbegrenzt ist, würde ein vollständiger Aufbau den Speicherbedarf von der größten Auswahl abhängig machen; beim Strömen bleibt er konstant.
\subsection{ZIP auch bei einer einzelnen Datei} \subsection{ZIP auch bei einer einzelnen Datei}
Im Review stellte \emph{Sarah Hinzmann} die Frage, ob bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle. Die Entscheidung fiel für das Archiv: Die Schaltfläche ist mit „Als ZIP herunterladen" beschriftet und soll vorhersagbar ein Archiv liefern. Wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der Zeile. Im Review fragte \emph{Sarah Hinzmann}, ob bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle. Die Entscheidung fiel für das Archiv: Die Schaltfläche „Als ZIP herunterladen" soll vorhersagbar ein Archiv liefern; wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der Zeile.
+3 -13
View File
@@ -3,22 +3,12 @@
\subsection{Filterung nach Dokumententyp} \subsection{Filterung nach Dokumententyp}
Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im PageModel ausgewertet; die Filterung selbst erfolgt anschließend im Dienst. Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im Dienst ausgewertet. Da die Werte aus der Adresszeile stammen, muss die Anwendung mit manipulierten oder veralteten Werten rechnen; \emph{Robin Noack} schlug vor, nicht erkennbare Werte stillschweigend zu verwerfen — ein unbekannter Filterwert ist in der Regel ein veralteter Link, und die Mandantentrennung geschieht über das Präfix, nicht über den Filter. Ein zweiter Reviewhinweis führte zu einer geschärften Benennung, da sich der Typ für eine Menge von Dokumententypen vom Einzeltyp nur durch ein Zeichen unterschied.
Die Auswertung des Parameters war Gegenstand des Reviews. Da die Werte aus der Adresszeile stammen, muss die Anwendung mit manipulierten oder veralteten Werten rechnen. \emph{Robin Noack} schlug vor, nicht erkennbare Werte stillschweigend zu verwerfen. Ein unbekannter Filterwert ist kein Angriff, sondern in der Regel ein veralteter Link. Die Mandantentrennung geschieht über das Präfix, nicht über den Filter.
Ein zweiter Reviewhinweis betraf die Benennung: Der Typ für eine Menge von Dokumententypen unterschied sich vom Einzeltyp nur durch ein Zeichen. Da eine Verwechslung leicht möglich ist, wurde die Benennung geschärft.
\subsection{Paginierung mit Fortsetzungsmerkmalen} \subsection{Paginierung mit Fortsetzungsmerkmalen}
Die Paginierung folgt der Arbeitsweise des Speichers, der Ergebnisse blockweise mit Fortsetzungsmerkmalen liefert \autocite{aws-listobjectsv2}. Ein Fortsetzungsmerkmal setzt die Auflistung dort fort, wo sie endete. Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen. Die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren Vor- und Zurück-Navigation. Die Paginierung folgt der Arbeitsweise des Speichers, der Ergebnisse blockweise mit Fortsetzungsmerkmalen liefert \autocite{aws-listobjectsv2}. Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen; die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren Vor- und Zurück-Navigation. Ein aus den bestehenden Houston-Seiten übernommener Wert für die maximale Seitengröße (NFA-4) stammte, wie \emph{Robin Noack} anmerkte, dort aus einer Beschränkung des ITSM-Systems und wurde angepasst. Diskutiert wurde auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte.
\subsection{Übernommene Annahmen}
Ein Zahlenwert für die maximale Seitengröße war aus den bestehenden Houston-Seiten übernommen worden (NFA-4). \emph{Robin Noack} wies darauf hin, dass dieser Wert dort aus einer Beschränkung des ITSM-Systems stammt und für den Speicher keine Bedeutung hat. Was wie eine Konvention aussah, war eine technische Grenze eines anderen Systems. Der Wert wurde angepasst.
Diskutiert wurde auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte, da ein sehr großer Wert aus der Adresszeile eine entsprechend große Antwort erzwingen könnte.
\subsection{Zusammenspiel mit Freigabelinks} \subsection{Zusammenspiel mit Freigabelinks}
Die Paginierung wirkt sich auf die Freigabelinks aus: Ein Link auf ein Dokument ist wertlos, wenn er stets auf der ersten Seite landet. Die Weiterleitung setzt deshalb nicht nur die Sprungmarke, sondern auch die Abfrageparameter für die richtige Seite. Diese Abhängigkeit war bei der Formulierung des Items berücksichtigt worden. Ein Freigabelink auf ein Dokument ist wertlos, wenn er stets auf der ersten Seite landet. Die Weiterleitung setzt deshalb nicht nur die Sprungmarke, sondern auch die Abfrageparameter für die richtige Seite.
+6 -16
View File
@@ -3,30 +3,20 @@
\subsection{Gemeinsame Umsetzung zweier Backlog Items} \subsection{Gemeinsame Umsetzung zweier Backlog Items}
Die automatische Ordneranlage und der neue Kundenordner-Lookup wurden in einem gemeinsamen Pull Request umgesetzt. Der Lookup benötigt in seiner Rückfallebene den Fall, dass kein Ordner existiert und die Struktur angelegt werden muss. Eine getrennte Implementierung hätte einen Platzhalter erfordert, der im nächsten Pull Request entfernt worden wäre. Die Zusammenlegung wurde begründet und beide Items verknüpft. Die automatische Ordneranlage und der neue Kundenordner-Lookup wurden in einem gemeinsamen, begründet zusammengelegten Pull Request umgesetzt, da der Lookup in seiner Rückfallebene den Fall benötigt, dass kein Ordner existiert und die Struktur angelegt werden muss.
\subsection{Normalisierung des Firmennamens} \subsection{Normalisierung des Firmennamens}
Der erwartete Ordnername entsteht aus dem Firmennamen des Benutzers. Da dieser aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er normalisiert: umschließende Leerzeichen entfernt, strukturell bedeutsame oder nicht darstellbare Zeichen ersetzt, führende und abschließende Punkte verhindert, die Länge begrenzt. Der erwartete Ordnername entsteht aus dem Firmennamen des Benutzers. Da dieser aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er normalisiert: umschließende Leerzeichen entfernt, strukturell bedeutsame oder nicht darstellbare Zeichen ersetzt, führende und abschließende Punkte verhindert, die Länge begrenzt. Die Normalisierung ist \textbf{deterministisch} — Voraussetzung für den Direktzugriff — und erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird. Im Review fragte \emph{Robin Noack}, ob die Einschränkungen ausreichen; die Zulässigkeit wurde anhand der Herstellerdokumentation bestätigt \autocite{ibm-s3-naming, aws-s3-naming}.
Zwei Eigenschaften sind entscheidend. Die Normalisierung ist \textbf{deterministisch} — derselbe Eingabename ergibt stets denselben Ordnernamen, Voraussetzung für den Direktzugriff. Sie erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird.
Im Review fragte \emph{Robin Noack}, ob die Einschränkungen ausreichen. Eine zu schwache Normalisierung führt zu Schlüsseln, die der Speicher zurückweist — das wäre erst bei einem Kunden mit ungewöhnlichem Firmennamen aufgefallen. Die Antwort verwies auf die Herstellerdokumentation \autocite{ibm-s3-naming, aws-s3-naming}.
\subsection{Umsetzung des Lookups} \subsection{Umsetzung des Lookups}
Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode zusammengefasst — der einzigen Stelle, an der ein Kundenpräfix entsteht. Der erste Schritt fragt die Metadaten des erwarteten Markers ab. Der zweite wiederholt dies mit dem um die Organisations-ID ergänzten Namen (Kollisionsfall). Erst der dritte greift auf das lineare Verfahren zurück. Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode zusammengefasst — der einzigen Stelle, an der ein Kundenpräfix entsteht. Der erste Schritt fragt die Metadaten des erwarteten Markers ab, der zweite wiederholt dies mit dem um die Organisations-ID ergänzten Namen (Kollisionsfall), erst der dritte greift auf das lineare Verfahren zurück. Beim Prüfen, ob ein Zielname frei ist, wird zusätzlich geprüft, ob bereits Objekte unterhalb des Zielpräfixes liegen; ein Zielname gilt nur als frei, wenn weder ein fremder Marker noch Inhalte vorhanden sind.
Beim Prüfen, ob ein Zielname frei ist, wird nicht nur der Marker betrachtet, sondern auch geprüft, ob bereits Objekte unterhalb des Zielpräfixes liegen. Ein Zielname gilt nur als frei, wenn weder ein fremder Marker noch Inhalte vorhanden sind.
\subsection{Umbenennen als Kopiervorgang} \subsection{Umbenennen als Kopiervorgang}
Der Speicher kennt keine Umbenennung. Ein Objekt lässt sich nur kopieren und anschließend löschen. Eine Umbenennung ist daher nicht unteilbar — sie kann abbrechen, und Objekte liegen teils am alten, teils am neuen Ort. Der Speicher kennt keine Umbenennung; ein Objekt lässt sich nur kopieren und anschließend löschen. Eine Umbenennung ist daher nicht unteilbar — sie kann abbrechen, und Objekte liegen teils am alten, teils am neuen Ort. Umgesetzt wurde dies über zwei Maßnahmen: Der \textbf{Marker wird zuletzt kopiert}, sodass das Ziel von einer parallelen Anfrage nicht als Kundenordner erkannt wird, solange er fehlt; und die Fehlerbehandlung entfernt bereits erzeugte Teilkopien, damit der Vorgang wiederholbar ist. Die zweite Maßnahme gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt — Gegenstand des folgenden Abschnitts.
Umgesetzt wurde dies über zwei Maßnahmen. Erstens wird der \textbf{Marker zuletzt kopiert}: Solange er fehlt, wird das Ziel von einer parallelen Anfrage nicht als Kundenordner erkannt. Zweitens entfernt die Fehlerbehandlung bereits erzeugte Teilkopien, damit der Vorgang wiederholbar ist. \subsection{Auslieferung}
Die zweite Maßnahme gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt. Dieser Fall ist Gegenstand des folgenden Abschnitts. Der zugehörige Pull Request wurde am 26.~August zusammengeführt, nachdem \emph{Robin Noack} zwei Anmerkungen eingebracht hatte: zur Ausreichung der Namensbeschränkungen und zu der Frage, ob ein Ordner vor seiner Erzeugung auf Fremdbesitz geprüft wird. Letztere ließ sich über die Zustände des Ordnermodells beantworten — erzeugt wird nur für einen Schlüssel im Zustand \emph{fehlend}. Mit der Produktivsetzung am 3.~September ist der Lookup mit konstanter Aufrufzahl wirksam; das lineare Verfahren dient nur noch als Rückfallebene.
\subsection{Stand bei Abgabe}
Der zugehörige Pull Request war zum Zeitpunkt der Erstellung dieser Dokumentation noch offen. Die inhaltlichen Anmerkungen waren geschlossen, die abschließende Freigabe stand aus.
+5 -17
View File
@@ -3,30 +3,18 @@
\subsection{PDF-Vorschau im Modal} \subsection{PDF-Vorschau im Modal}
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt (Abbildung~\ref{fig:shot-pdf-preview} im Anhang). Für Nicht-PDF-Dateien wird keine Vorschau angeboten. Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt (Abbildung~\ref{fig:shot-pdf-preview} im Anhang); für Nicht-PDF-Dateien wird keine Vorschau angeboten. Eine Prüfung der Dateigröße wurde im Approval-Termin bewusst verworfen, da jede Grenze willkürlich wäre und der Browser PDF-Dokumente ohnehin fortlaufend darstellt.
Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung wurde im Approval-Termin festgelegt: Jede Grenze wäre willkürlich, und der Browser stellt PDF-Dokumente ohnehin fortlaufend dar.
\subsection{Aufbau der Freigabelinks} \subsection{Aufbau der Freigabelinks}
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren. Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet zu transportieren. Dass der Kundenordner nicht Bestandteil des Links ist (Listing~\ref{lst:document-id}), bewirkt, dass der Link keinen Hinweis auf die Organisation enthält und ohne Anmeldung nicht auflösbar ist. Die Seite zeigt keinen Dokumentinhalt, sondern liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
Dass der Kundenordner nicht Bestandteil des Links ist (Listing~\ref{lst:document-id}), hat eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne Anmeldung nicht auflösbar.
Die Seite zeigt keinen Dokumentinhalt an. Sie liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
\subsection{Vorschau in Messengern} \subsection{Vorschau in Messengern}
Der Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Abbildung~\ref{fig:shot-share-teams} im Anhang zeigt das Ergebnis in Microsoft Teams; Abbildung~\ref{fig:shot-share-target} das Ziel des Links in Houston. Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Abbildung~\ref{fig:shot-share-teams} im Anhang zeigt das Ergebnis in Microsoft Teams, Abbildung~\ref{fig:shot-share-target} das Ziel in Houston. Da die Typsymbole als Vektorgrafiken vorliegen, die Vorschaudienste gängiger Messenger nicht zuverlässig darstellen, mussten sie zusätzlich als Rastergrafiken bereitgestellt werden; im Pull Request wurde vermerkt, dass diese bei einer Gestaltungsänderung nachzuziehen sind.
Die Typsymbole liegen als Vektorgrafiken vor, die von Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt werden. Sie mussten daher zusätzlich als Rastergrafiken bereitgestellt werden. Im Pull Request wurde vermerkt, dass die Rastergrafiken bei einer Gestaltungsänderung nachzuziehen sind.
\subsection{Sichtbarkeit der Vorschaubilder} \subsection{Sichtbarkeit der Vorschaubilder}
Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Der Freigabe-Endpunkt ist deshalb anonym erreichbar — und damit die einzige Stelle des Moduls ohne Autorisierung. Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Der Freigabe-Endpunkt ist deshalb anonym erreichbar — die einzige Stelle des Moduls ohne Autorisierung. Entschärft wird das durch den Aufbau der Dokumentkennung: Sie ist die Base64-Kodierung des relativen Pfades und trägt bereits alles, was die Seite ausgeben muss — Dokumentname als Titel, Dokumententyp und daraus den Pfad des Vorschaubildes sowie die Zieladresse (Listing~\ref{lst:share-page}). Der anonyme Endpunkt löst folglich keinen Aufruf an den Objektspeicher aus; er kann weder die Existenz eines Dokuments bestätigen noch Inhalte preisgeben noch als Hebel für Last dienen.
Entschärft wird das durch den Aufbau der Dokumentkennung: Sie ist die Base64-Kodierung des relativen Pfades und trägt damit bereits alles, was die Seite ausgeben muss. Aus dem dekodierten Pfad ergeben sich Dokumentname als Titel, Dokumententyp und daraus der Pfad des Vorschaubildes sowie die Zieladresse in der Dokumentenliste (Listing~\ref{lst:share-page}). Der anonyme Endpunkt löst folglich keinen einzigen Aufruf an den Objektspeicher aus. Er kann weder die Existenz eines Dokuments bestätigen noch Inhalte preisgeben, und er ist auch nicht als Hebel geeignet, um über wiederholte Aufrufe Last auf dem Speicher zu erzeugen. Bei den sieben festen Typsymbolen ist die freie Abrufbarkeit unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden. Die Entscheidung wurde im Pull Request festgehalten.
Bei den sieben festen Typsymbolen ist die freie Abrufbarkeit unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden, und die genannte Eigenschaft ginge verloren.
Die Entscheidung wurde im Pull Request festgehalten. Sie zeigt, dass eine harmlose Funktion in Verbindung mit einer anderen eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte.
+43 -30
View File
@@ -3,13 +3,11 @@
\subsection{Ausgangslage} \subsection{Ausgangslage}
Der Lesepfad des Kundenordner-Lookups ist unkritisch. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an, im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, reicht eine Sperre innerhalb einer Instanz nicht aus. Der Lesepfad des Kundenordner-Lookups ist unkritisch. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an, im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, reicht eine Sperre innerhalb einer Instanz nicht aus. Bei der Durchsicht des Codes wurden zwei Fehlerszenarien gefunden, die ausdrücklich formulierte Akzeptanzkriterien verletzen.
Bei der Durchsicht des Codes wurden zwei Fehlerszenarien gefunden, die ausdrücklich formulierte Akzeptanzkriterien verletzen. \subsection{Zwei Szenarien}
\subsection{Szenario A: Datenverlust beim gleichzeitigen Umbenennen} Abbildung~\ref{fig:race-condition-a} zeigt exemplarisch Szenario~A; Tabelle~\ref{tab:race-conditions} stellt beide Fälle einander gegenüber.
Das erste Szenario betrifft die Fehlerbehandlung des Umbenennens. Abbildung~\ref{fig:race-condition-a} zeigt den Ablauf.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -18,37 +16,52 @@ Das erste Szenario betrifft die Fehlerbehandlung des Umbenennens. Abbildung~\ref
\label{fig:race-condition-a} \label{fig:race-condition-a}
\end{figure} \end{figure}
Beide Anfragen halten den Zielnamen für frei, weil beide prüfen, bevor eine schreibt. Anfrage~A kopiert alle Objekte und löscht die Quelle. Anfrage~B findet beim nächsten Objekt die Quelle nicht mehr vor. Ihre Fehlerbehandlung entfernt die eigenen Teilkopien — dieselben Objekte, die A soeben geschrieben hat. Da die Quelle gelöscht ist, existiert keine Kopie mehr. Das Ergebnis ist Datenverlust. \begin{table}[H]
\centering
\begin{tabularx}{\textwidth}{@{} l X X @{}}
\toprule
\textbf{Szenario} & \textbf{Ursache} & \textbf{Auswirkung} \\
\midrule
A: Datenverlust beim Umbenennen &
Zwei Anfragen halten den Zielnamen für frei, da beide prüfen, bevor eine schreibt. A kopiert und löscht die Quelle; B's Fehlerbehandlung entfernt daraufhin dieselben (bereits von A geschriebenen) Objekte. &
Quelle und Kopie sind gelöscht — Datenverlust. \\
\addlinespace
B: Vermischung zweier Mandanten &
Der Marker wird ohne Bedingung geschrieben. Haben zwei Organisationen denselben Firmennamen, gewinnt beim gleichzeitigen Erstzugriff der zuletzt geschriebene Marker. &
Beide arbeiten im selben Ordner; zwischenzeitlich Abgelegtes bleibt im fremden Ordner — Vertraulichkeitsproblem, schwerwiegender als A. \\
\bottomrule
\end{tabularx}
\caption{Erkannte Nebenläufigkeitsszenarien im Kundenordner-Lookup}
\label{tab:race-conditions}
\end{table}
\subsection{Szenario B: Vermischung zweier Mandanten} Nicht betroffen sind zwei gleichzeitige Anfragen derselben Organisation (identische Schlüssel) sowie Lesezugriffe während einer Verschiebung, da der Marker zuletzt kopiert wird. Das Problem beschränkt sich auf zwei \emph{unterschiedlich weit fortgeschrittene} verändernde Vorgänge.
Der Marker wird ohne Bedingung geschrieben. Haben zwei Organisationen denselben Firmennamen und greifen erstmals gleichzeitig zu, legen beide den Marker an; der zuletzt geschriebene gewinnt. Beide arbeiten anschließend im selben Ordner, obwohl das Metadatum nur einer gehört. Der Folgeaufruf korrigiert sich zwar selbst, doch zwischenzeitlich Abgelegtes verbleibt im fremden Ordner.
Von den beiden Szenarien ist dieses das schwerwiegendere: Datenverlust ist ein Betriebsproblem, die Offenlegung von Kundendokumenten ein Vertraulichkeitsproblem.
\subsection{Abgegrenzte Fälle}
Zur Eingrenzung wurde geprüft, welche nebenläufigen Abläufe \emph{nicht} betroffen sind. Zwei Anfragen derselben Organisation erzeugen identische Schlüssel und sind harmlos. Lesen während einer Verschiebung ist abgedeckt: Da der Marker zuletzt kopiert wird, wird ein halb gefülltes Ziel nicht als Kundenordner erkannt. Das Problem beschränkt sich auf zwei \emph{unterschiedlich weit fortgeschrittene} verändernde Vorgänge.
\subsection{Erwogene Gegenmaßnahmen} \subsection{Erwogene Gegenmaßnahmen}
Vier Ansätze wurden formuliert: Tabelle~\ref{tab:race-countermeasures} stellt die vier erwogenen Ansätze gegenüber; der erste sollte unabhängig von der Serialisierungslösung umgesetzt werden, der vierte ist eine organisatorische statt technische Lösung.
\begin{enumerate} \begin{table}[H]
\item \textbf{Absicherung der Fehlerbehandlung.} Vor dem Entfernen der Teilkopien wird geprüft, ob der Marker der Quelle noch existiert. Fehlt er, hat ein anderer Vorgang die Umbenennung abgeschlossen, und es darf nichts gelöscht werden. Das beseitigt Szenario~A weitgehend: Es bleiben überzählige Objekte zurück statt Daten verloren zu gehen. \centering
\item \textbf{Bedingtes Schreiben.} Wird der Marker nur unter der Bedingung geschrieben, dass er noch nicht existiert, ist das Beanspruchen eines Namens unteilbar und Szenario~B ausgeschlossen \autocite{aws-conditional-writes}. Voraussetzung ist, dass der Speicher diese vergleichsweise junge Erweiterung unterstützt — was ausdrücklich nachzuweisen wäre. \begin{tabularx}{\textwidth}{@{} l X @{}}
\item \textbf{Instanzübergreifende Sperre.} Eine über die Datenbank realisierte Sperre je Organisation würde den verändernden Teil sauber serialisieren, kostet aber einen zusätzlichen Zugriff je Anfrage und erfordert ein Konzept für den Fall, dass eine Sperre nicht freigegeben wird. \toprule
\item \textbf{Verlagerung aus dem Anfragepfad.} Bestandsordner werden einmalig kontrolliert migriert, und die Anwendung protokolliert lediglich, wenn ein Ordner vom Sollzustand abweicht. Damit verschwindet die Ursache vollständig statt abgesichert zu werden. \textbf{Ansatz} & \textbf{Wirkung} \\
\end{enumerate} \midrule
Absicherung der Fehlerbehandlung & Prüfung, ob der Marker der Quelle vor dem Entfernen der Teilkopien noch existiert; beseitigt A weitgehend, es bleiben überzählige Objekte statt Datenverlust. \\
Der erste Ansatz sollte unabhängig von der Serialisierungslösung umgesetzt werden. Der vierte ist bemerkenswert, da er eine organisatorische statt technische Lösung darstellt. \addlinespace
Bedingtes Schreiben & Marker nur schreiben, falls noch nicht vorhanden, macht das Beanspruchen eines Namens unteilbar und schließt B aus \autocite{aws-conditional-writes}; setzt Unterstützung durch den Speicher voraus. \\
\addlinespace
Instanzübergreifende Sperre & Sperre je Organisation über die Datenbank serialisiert den verändernden Teil sauber, kostet aber einen zusätzlichen Zugriff je Anfrage und ein Konzept für nicht freigegebene Sperren. \\
\addlinespace
Verlagerung aus dem Anfragepfad & Einmalige kontrollierte Migration der Bestandsordner; die Anwendung protokolliert nur noch Abweichungen vom Sollzustand — die Ursache verschwindet vollständig. \\
\bottomrule
\end{tabularx}
\caption{Erwogene Gegenmaßnahmen gegen die Nebenläufigkeitsszenarien}
\label{tab:race-countermeasures}
\end{table}
\subsection{Umgang mit dem Befund} \subsection{Umgang mit dem Befund}
Der Befund wurde als eigenes Backlog Item mit erhöhter Priorität erfasst und vom laufenden Pull Request abgegrenzt. Das Team versah es mit einem Zeitrahmen von vier bis sechs Stunden. Der Befund wurde als eigenes Backlog Item mit erhöhter Priorität erfasst, vom laufenden Pull Request abgegrenzt und mit einem Zeitrahmen von vier bis sechs Stunden versehen.
Den Pull Request offenzuhalten, bis die Nebenläufigkeit gelöst ist, hätte den Lookup blockiert: Ob bedingtes Schreiben verfügbar ist, ließ sich ohne Rückfrage beim Betreiber nicht beantworten, und deren Bearbeitungsdauer war zuvor mit mehreren Wochen bemessen worden. Der Lookup verbessert den Zustand gegenüber vorher bereits deutlich. Den Pull Request offenzuhalten, hätte den Lookup blockiert: Ob bedingtes Schreiben verfügbar ist, ließ sich ohne Rückfrage beim Betreiber nicht beantworten, deren Bearbeitungsdauer zuvor mit mehreren Wochen bemessen worden war. Der Lookup verbessert den Zustand bereits deutlich, und der verändernde Pfad wird nur beim ersten Zugriff einer Organisation durchlaufen — das Risikofenster besteht pro Kunde genau einmal. Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz nicht akzeptiert wird und beide Szenarien durch Unit-Tests nachzubilden sind.
Hinzu kommt, dass der verändernde Pfad nur beim ersten Zugriff einer Organisation durchlaufen wird. Das Risikofenster besteht pro Kunde genau einmal.
Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz nicht als Lösung akzeptiert wird und dass beide Szenarien durch Unit-Tests nachzubilden sind.
+3 -9
View File
@@ -3,15 +3,13 @@
\subsection{Zugriff über das AWS SDK} \subsection{Zugriff über das AWS SDK}
Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über eine abweichende Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten. Eine eigene Implementierung — insbesondere der Signaturberechnung — wäre aufwendig und fehleranfällig gewesen. Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über eine abweichende Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten; eine eigene Implementierung der Signaturberechnung wäre aufwendig und fehleranfällig gewesen.
Der \texttt{S3DocumentsClient} kapselt die verwendeten Operationen: Auflisten, Metadatenabruf, Erzeugen zeitlich begrenzter Zugriffs-URLs, Lesen von Objektinhalten sowie Anlegen, Kopieren und Löschen von Objekten. Diese Kapselung hält die SDK-spezifischen Typen aus der fachlichen Schicht und ermöglicht in Tests ein Mock (siehe Abschnitt~\ref{sec:unit-tests}). Der \texttt{S3DocumentsClient} kapselt die verwendeten Operationen: Auflisten, Metadatenabruf, Erzeugen zeitlich begrenzter Zugriffs-URLs, Lesen von Objektinhalten sowie Anlegen, Kopieren und Löschen von Objekten. Diese Kapselung hält die SDK-spezifischen Typen aus der fachlichen Schicht und ermöglicht in Tests ein Mock (siehe Abschnitt~\ref{sec:unit-tests}).
\subsection{Konfiguration mehrerer Speicher} \subsection{Konfiguration mehrerer Speicher}
Houston verwendete bereits vor diesem Projekt einen S3-Speicher für das Dokumentationssystem. Mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger Speicher hinzu. Houston verwendete bereits vor diesem Projekt einen S3-Speicher für das Dokumentationssystem; mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger hinzu. In der ersten Fassung wurden die Einstellungen als eigenständiger Konfigurationssatz geführt. Im Review wurde angeregt, eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}. Ein dritter Speicher erfordert so lediglich einen weiteren Konfigurationsabschnitt und eine Registrierung.
In der ersten Fassung wurden die Einstellungen als eigenständiger Konfigurationssatz geführt. Im Review wurde angeregt, eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}. Ein dritter Speicher erfordert so lediglich einen weiteren Konfigurationsabschnitt und eine Registrierung, nicht aber eine Verdopplung der Einstellungsklassen.
\subsection{Umgebungen und Zugangsdaten} \subsection{Umgebungen und Zugangsdaten}
@@ -19,8 +17,4 @@ Für die drei Umgebungen existiert je ein eigener Bucket mit getrennten Zugangsd
\subsection{Auflisten von Objekten} \subsection{Auflisten von Objekten}
Die zentrale Leseoperation listet Objekte unterhalb eines Präfixes auf. Sie wird mit dem Kundenpräfix aufgerufen und liefert sämtliche Objekte über alle Typordner hinweg. Die zentrale Leseoperation listet Objekte unterhalb des Kundenpräfixes über alle Typordner hinweg auf. Zwei Eigenschaften prägten die Umsetzung: Erstens liefert sie Ergebnisse blockweise mit einem Fortsetzungsmerkmal \autocite{aws-listobjectsv2}, das für die Paginierung genutzt wurde (siehe Abschnitt~\ref{sec:filter-pagination}); zweitens liefert sie keine benutzerdefinierten Metadaten — die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems. Platzhalterobjekte der Typordner und der Marker des Kundenordners werden herausgefiltert; beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
Zwei Eigenschaften prägten die Umsetzung. Erstens liefert die Operation Ergebnisse blockweise: Überschreitet die Trefferzahl eine Grenze, wird ein Fortsetzungsmerkmal zurückgegeben \autocite{aws-listobjectsv2}. Diese Eigenschaft wurde für die Paginierung genutzt (siehe Abschnitt~\ref{sec:filter-pagination}). Zweitens liefert sie keine benutzerdefinierten Metadaten — die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems.
Beim Auflisten werden Platzhalterobjekte der Typordner und der Marker des Kundenordners herausgefiltert. Beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
+3 -9
View File
@@ -7,16 +7,10 @@ Die Suche filtert die Dokumentenliste anhand des Namens und berücksichtigt Teil
\subsection{Der Begriff „serverseitig"} \subsection{Der Begriff „serverseitig"}
Die Anforderung verlangt eine serverseitige Suche. Gemeint ist, dass die Filterung in der Anwendung stattfindet, nicht im Browser — der Browser erhält die bereits gefilterte Menge. Eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden, was mit der Paginierung unvereinbar wäre. Die Anforderung verlangt eine serverseitige Suche. Gemeint ist, dass die Filterung in der Anwendung stattfindet, nicht im Browser — eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden, was mit der Paginierung unvereinbar wäre. Nicht umgesetzt ist eine Filterung durch den \emph{Speicher}: Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte \emph{Hanna Ebner} die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.
Nicht umgesetzt ist eine Filterung durch den \emph{Speicher}. Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte \emph{Hanna Ebner} die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden. Im weiteren Verlauf brachte \emph{Timo Walter} die Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Objektinhalte filtert, nicht Objektnamen — sie wählt aus strukturierten Dateien einzelne Datensätze aus \autocite{aws-s3-select}. Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen \autocite{storagegrid-search-integration}; dessen Verfügbarkeit stand noch nicht fest, die Anfrage lief bereits (siehe Abschnitt~\ref{sec:infrastructure}) und fiel später negativ aus. Auf Anregung des Reviewers wurde dieser Umstand in das Recherche-Item aufgenommen.
\subsection{Warum keine Filterung im Speicher möglich war}
Im weiteren Verlauf brachte \emph{Timo Walter} die Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Objektinhalte filtert, nicht Objektnamen — sie wählt aus strukturierten Dateien einzelne Datensätze aus \autocite{aws-s3-select}.
Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen \autocite{storagegrid-search-integration}. Dessen Verfügbarkeit stand noch nicht fest; die Anfrage lief bereits (siehe Abschnitt~\ref{sec:infrastructure}). Die spätere Antwort fiel negativ aus. Auf Anregung des Reviewers wurde dieser Umstand in das Recherche-Item aufgenommen.
\subsection{Bewertung} \subsection{Bewertung}
Die praktische Auswirkung ist derzeit gering. Die Dokumentenzahl je Kunde ist überschaubar; die Suche skaliert nur mit der Dokumentenzahl eines einzelnen Kunden, nicht mit der Kundenzahl. Der Lookup wurde daher im Projektzeitraum gelöst, die Suchoptimierung blieb für den Ausblick. Die praktische Auswirkung ist derzeit gering, da die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert, nicht mit der Kundenzahl. Der Lookup wurde daher im Projektzeitraum gelöst, die Suchoptimierung blieb für den Ausblick.
+3 -7
View File
@@ -3,16 +3,12 @@
\subsection{Eigene Symbole statt Symbolschrift} \subsection{Eigene Symbole statt Symbolschrift}
Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt. Houston verwendet für Symbole eine gängige Symbolbibliothek, die für die sieben Dokumententypen keine passenden Motive enthielt — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt. Die Entwürfe stammen von mir und wurden in Abstimmung mit \emph{Hanna Ebner} ausgearbeitet, damit sie sich in Strichstärke, Abmessung und Bildsprache in die vorhandene Symbolbibliothek einfügen; alle greifen dieselbe Dokumentkontur auf und unterscheiden sich nur im Innenmotiv, sodass die Typzugehörigkeit auf einen Blick erkennbar ist (Abbildung~\ref{fig:type-icons} im Anhang).
Die Entwürfe stammen von mir und wurden in Abstimmung mit \emph{Hanna Ebner} ausgearbeitet, damit sie sich in Strichstärke, Abmessung und Bildsprache in die vorhandene Symbolbibliothek einfügen. Alle Symbole greifen dieselbe Dokumentkontur auf und unterscheiden sich nur im Innenmotiv, sodass die Typzugehörigkeit auf einen Blick erkennbar ist, ohne dass die Zeilen unruhig wirken. Abbildung~\ref{fig:type-icons} im Anhang zeigt den vollständigen Satz.
Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und fest eingefärbte Symbole wären im dunklen kaum sichtbar gewesen.
\subsection{Einfärbung über Masken} \subsection{Einfärbung über Masken}
Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfläche dienen. Die Fläche übernimmt die aktuelle Textfarbe, sodass sich das Symbol automatisch anpasst. Die eigenen Symbole lassen sich über dieselben Gestaltungsklassen ansprechen wie die vorhandenen. Alle Grafiken müssen dieselben Abmessungen haben; im Review fiel auf, dass eine abwich, und die Maße wurden angeglichen. Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet, die im dunklen Erscheinungsbild von Houston kaum sichtbar gewesen wäre. Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfläche dienen, die die aktuelle Textfarbe übernimmt; die eigenen Symbole lassen sich so über dieselben Gestaltungsklassen ansprechen wie die vorhandenen. Alle Grafiken müssen dieselben Abmessungen haben; im Review fiel auf, dass eine abwich, und die Maße wurden angeglichen.
\subsection{Standardsymbol} \subsection{Standardsymbol}
Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol (letzter Eintrag in Abbildung~\ref{fig:type-icons}). Damit trägt jede Zeile ein Symbol, und an der Darstellung ist erkennbar, dass keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt. Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol (letzter Eintrag in Abbildung~\ref{fig:type-icons}). Damit trägt jede Zeile ein Symbol, und die fehlende Typzuordnung ist erkennbar, ohne wie ein Fehler zu wirken.
+6 -20
View File
@@ -3,32 +3,18 @@
\subsection{Anwendungsfall} \subsection{Anwendungsfall}
Nicht alle Unterlagen lassen sich sinnvoll in den Speicher kopieren. Ein fortlaufend gepflegtes Dokument oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt werden — eine Kopie wäre sofort veraltet. Dafür werden \texttt{.url}-Dateien unterstützt: kleine Textdateien im INI-Format mit Zieladresse und optionalem Symbol. In der Liste erscheinen sie wie gewöhnliche Dokumente, führen beim Anklicken jedoch auf die hinterlegte Adresse. Nicht alle Unterlagen lassen sich sinnvoll in den Speicher kopieren; ein fortlaufend gepflegtes Dokument oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt werden, da eine Kopie sofort veraltet wäre. Dafür werden \texttt{.url}-Dateien unterstützt: kleine Textdateien im INI-Format mit Zieladresse und optionalem Symbol, die in der Liste wie gewöhnliche Dokumente erscheinen, beim Anklicken jedoch auf die hinterlegte Adresse führen. Der Anwendungsfall wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}).
Der Anwendungsfall wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}). \subsection{Auswertung und Anzeigename}
\subsection{Auswertung des Dateiformats} Für das INI-Format wurde bewusst keine externe Bibliothek eingebunden: Benötigt wird ein einziger Wert aus einem Abschnitt, der in wenigen Zeilen selbst gelesen ist, und eine Bibliothek würde bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist. Kann keine gültige Adresse erkannt werden, wird die Datei wie eine gewöhnliche Datei zum Herunterladen angeboten. Die Endung \texttt{.url} wird für die Anzeige entfernt; \emph{Robin Noack} wies im Review darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand und stattdessen die Länge der Endung selbst verwendet werden sollte, da beide sonst bei einer Änderung auseinanderlaufen.
Für das INI-Format wurde bewusst keine externe Bibliothek eingebunden. Benötigt wird ein einziger Wert aus einem Abschnitt. Eine Bibliothek brächte eine dauerhaft zu pflegende Abhängigkeit für Funktionalität, die in wenigen Zeilen selbst geschrieben ist. Zudem würde sie bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist.
\subsection{Rückfall auf normales Verhalten}
Kann keine gültige Adresse erkannt werden, wird die Datei wie eine gewöhnliche Datei zum Herunterladen angeboten. Eine fehlerhafte Verknüpfungsdatei darf das Dokument nicht unzugänglich machen.
\subsection{Anzeigename}
Die Endung \texttt{.url} wird für die Anzeige entfernt. Im Review wies \emph{Robin Noack} darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand; stattdessen sollte die Länge der Endung selbst verwendet werden, da Zahl und Endung an verschiedenen Stellen stehen und bei einer Änderung auseinanderlaufen können.
\subsection{Symbole für Verknüpfungen} \subsection{Symbole für Verknüpfungen}
Das Dateiformat sieht im Abschnitt \texttt{[InternetShortcut]} einen Eintrag \texttt{IconFile} vor, der unter Windows auf eine Symboldatei zeigt \autocite{nsis-shortcuts}. Ein Verweis auf eine Datei ist im Browser nicht verwertbar, und aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen werden benutzerdefinierte Symbole ohnehin nicht ausgeliefert. Der Eintrag wird daher umgedeutet: Er trägt keinen Dateipfad, sondern den Namen eines Symbols, das die Anwendung bereits kennt. Das Dateiformat sieht im Abschnitt \texttt{[InternetShortcut]} einen Eintrag \texttt{IconFile} vor, der unter Windows auf eine Symboldatei zeigt \autocite{nsis-shortcuts}. Ein Dateipfad ist im Browser nicht verwertbar, und aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen werden benutzerdefinierte Symbole ohnehin nicht ausgeliefert. Der Eintrag wird daher umgedeutet: Er trägt den Namen eines Symbols, das die Anwendung bereits kennt. Zulässig sind die Klassen der eigenen Typsymbole, etwa \texttt{doc-type-icon-vertragsunterlagen}, sowie Klassen der in Houston vorhandenen Symbolbibliothek Boxicons, etwa \texttt{bx bxl-github} \autocite{boxicons}. Für die drei häufigsten Verknüpfungsziele — Microsoft Teams, SharePoint und OneDrive — wurden im selben Stil eigene Grafiken ergänzt (Abbildung~\ref{fig:type-icons} im Anhang).
Zwei Formen sind zulässig. Zum einen die Klassen der eigenen Typsymbole, etwa \texttt{doc-type-icon-vertragsunterlagen}; zum anderen eine Klasse der in Houston vorhandenen Symbolbibliothek Boxicons, etwa \texttt{bx bxl-github} \autocite{boxicons}. Für die drei häufigsten Verknüpfungsziele — Microsoft Teams, SharePoint und OneDrive — wurden im selben Stil wie die Typsymbole eigene Grafiken ergänzt (Abbildung~\ref{fig:type-icons} im Anhang). Fehlt der Eintrag oder ist er leer, greift dieselbe Regel wie für gewöhnliche Dateien: Liegt die Verknüpfung in einem bekannten Typordner, erscheint dessen Symbol, sonst das Standardsymbol; ein unbekannter Wert führt damit nie zu einer leeren Zelle (Abbildung~\ref{fig:shot-url-icons} im Anhang). Da die Kundenbetreuer diese Dateien von Hand anlegen, wurden Format, zulässige Werte und Rückfallverhalten im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
Fehlt der Eintrag oder ist er leer, greift dieselbe Regel wie für gewöhnliche Dateien: Liegt die Verknüpfung in einem bekannten Typordner, erscheint dessen Symbol, sonst das Standardsymbol. Ein unbekannter Wert führt damit nie zu einer leeren Zelle. Abbildung~\ref{fig:shot-url-icons} im Anhang zeigt die Fälle nebeneinander.
Da die Kundenbetreuer diese Dateien von Hand anlegen, wurden Format, zulässige Werte und Rückfallverhalten im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
\subsection{Ausschluss vom ZIP-Download} \subsection{Ausschluss vom ZIP-Download}
Verknüpfungsdateien erhalten keine Auswahlbox und können nicht Teil eines Archivs werden. Ein Archiv enthielte sonst eine Datei, die beim Öffnen eine möglicherweise nur intern auflösbare Verknüpfung liefert. Da eine Verknüpfung fachlich kein Dokument ist, wäre ihre Aufnahme irreführend. Verknüpfungsdateien erhalten keine Auswahlbox und können nicht Teil eines Archivs werden, das sonst eine Datei mit einer möglicherweise nur intern auflösbaren Verknüpfung enthielte. Da eine Verknüpfung fachlich kein Dokument ist, wäre ihre Aufnahme irreführend.
+2 -11
View File
@@ -1,15 +1,6 @@
\section{Ausgangssituation} \section{Ausgangssituation}
\label{sec:initial-situation} \label{sec:initial-situation}
Die Idee 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 unverändert im Backlog, ohne einen konkreten Lösungsansatz zu beschreiben. Die Idee besteht seit November 2023: \emph{Nicole Kimmel} legte damals das Feature~484 „Dokumente" mit zwei Stichpunkten an, ohne einen Lösungsansatz zu beschreiben.
Zum Projektbeginn im Sommer 2026 wurden Verträge, Berichte und ähnliche Unterlagen punktuell per E-Mail oder Dateiablage bereitgestellt. Daraus ergaben sich mehrere Probleme: Zum Projektbeginn im Sommer 2026 wurden Verträge, Berichte und ähnliche Unterlagen punktuell per E-Mail oder Dateiablage bereitgestellt. Sie lagen ohne einheitlichen Zugangspunkt verteilt, mussten von Kunden aktiv angefordert werden und boten weder einen Überblick nach Dokumententyp noch einen technisch mandantengetrennten Zugriff. Zudem war kein S3-Speicher provisioniert; die Infrastruktur — drei Buckets (DEV, TEST, PROD) bei Advanced Unibyte auf Basis von NetApp StorageGRID — musste erst im Projektverlauf beantragt werden.
\begin{itemize}
\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.
\end{itemize}
Auf technischer Seite war kein S3-Speicher provisioniert. Die Bereitstellung der notwendigen Infrastruktur — drei S3-Buckets (DEV, TEST, PROD) bei Advanced Unibyte auf Basis von NetApp StorageGRID — musste erst im Projektverlauf beantragt werden.
+2 -4
View File
@@ -3,11 +3,9 @@
Als \textbf{Praktikant und Entwickler} (\emph{Linus Nagel}) war ich für Anforderungsklärung, Konzeption, Implementierung aller Product Backlog Items, Akzeptanzkriterien und Dokumentation verantwortlich. Als \textbf{Praktikant und Entwickler} (\emph{Linus Nagel}) war ich für Anforderungsklärung, Konzeption, Implementierung aller Product Backlog Items, Akzeptanzkriterien und Dokumentation verantwortlich.
\emph{Sarah Hinzmann} übernahm die \textbf{betriebliche Betreuung}, koordinierte Abstimmungen und war an Code-Reviews der Abschlussphase beteiligt. \emph{Thomas Drewermann} fungierte als \textbf{Product Owner}: Er arbeitete das Feature~484 im Juni 2026 aus, definierte den Umfang und beantwortete Rückfragen. \emph{Sarah Hinzmann} übernahm die \textbf{betriebliche Betreuung}, koordinierte Abstimmungen und war an Code-Reviews der Abschlussphase beteiligt. \emph{Thomas Drewermann} war \textbf{Product Owner}: Er arbeitete Feature~484 im Juni 2026 aus, definierte den Umfang und beantwortete Rückfragen.
Die \textbf{technische Qualitätssicherung} lag beim Team der Unicorn Development: \emph{Timo Walter} (Code-Reviews der frühen PRs, Architektur-Rückfragen), \emph{Robin Noack} (Reviews der Schlussphase), \emph{Hanna Ebner} (Reviews sowie Entscheidungen zur Oberfläche, Clickdummy der Typfilterung). \emph{Maria-Lena Andersz} führte die \textbf{Abnahmetests} durch. Die \textbf{technische Qualitätssicherung} lag beim Team der Unicorn Development: \emph{Timo Walter} (Code-Reviews der frühen PRs, Architektur-Rückfragen), \emph{Robin Noack} (Reviews der Schlussphase) und \emph{Hanna Ebner} (Reviews, Oberflächenentscheidungen, Clickdummy der Typfilterung). \emph{Maria-Lena Andersz} führte die \textbf{Abnahmetests} durch. Weitere Beteiligte: \emph{Stephan Janßen} (Schätzung, Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung) und \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023).
Weitere Beteiligte: \emph{Stephan Janßen} (Schätzung, Backlog-Pflege), \emph{Christiana Sobik} (Backlog-Pflege), \emph{Bianco Veigel} (Sprint-Planung), \emph{Nicole Kimmel} (ursprüngliche Anforderung, 2023).
\begin{table}[H] \begin{table}[H]
\centering \centering
+3 -3
View File
@@ -1,8 +1,8 @@
\section{Projektbeschreibung} \section{Projektbeschreibung}
\label{sec:project-description} \label{sec:project-description}
Das Projekt \emph{Houston Dokumente} stellt Kunden im Kundenportal Houston einen zentralen Bereich bereit, in dem sie ihre Dokumente einsehen und herunterladen können. Die Dokumente werden in einem S3-Speicher abgelegt; Mitarbeiter pflegen sie über das interne Dateiverwaltungswerkzeug Filestash, Kunden rufen sie über eine neue Houston-Seite strukturiert ab. Das Projekt \emph{Houston Dokumente} stellt Kunden im Kundenportal Houston einen zentralen Bereich bereit, in dem sie ihre Dokumente einsehen und herunterladen können. Die Dokumente liegen in einem S3-Speicher; Mitarbeiter pflegen sie über das interne Werkzeug Filestash, Kunden rufen sie über eine neue Houston-Seite strukturiert ab.
Der Dokumentenbereich unterscheidet sieben fachlich definierte Dokumententypen — darunter Service-Protokolle, SLA-Reports, Monitoring Reports und Vertragsunterlagen. Weitere Funktionen sind Suche, Typfilter, Paginierung, PDF-Viewer, Einzel- und ZIP-Download sowie URL-Dateien, mit denen beliebige Webadressen in der Dokumentenliste verknüpft werden können. Der Dokumentenbereich unterscheidet sieben fachlich definierte Dokumententypen — darunter Service-Protokolle, SLA-Reports, Monitoring Reports und Vertragsunterlagen — und bietet Suche, Typfilter, Paginierung, PDF-Viewer, Einzel- und ZIP-Download sowie URL-Dateien (siehe Abschnitt~\ref{sec:target-situation}).
Technisch umfasst das Projekt die Anbindung an den S3-Speicher über das AWS SDK für .NET, ein rollenbasiertes Berechtigungskonzept über Microsoft Entra~ID sowie die automatische Anlage der Ordnerstruktur beim ersten Seitenaufruf. Schreibzugriffe durch Kunden sind nicht vorgesehen; dies bleibt Aufgabe der internen Mitarbeiter. Technisch umfasst das Projekt die S3-Anbindung über das AWS SDK für .NET, ein rollenbasiertes Berechtigungskonzept über Microsoft Entra~ID sowie die automatische Ordneranlage beim ersten Seitenaufruf. Schreibzugriffe durch Kunden sind nicht vorgesehen.
+4 -4
View File
@@ -4,8 +4,8 @@
Folgende Punkte sind explizit \emph{nicht} Bestandteil des Projekts: Folgende Punkte sind explizit \emph{nicht} Bestandteil des Projekts:
\begin{itemize} \begin{itemize}
\item \textbf{Kein Schreibzugriff für Kunden:} Kunden können Dokumente ausschließlich einsehen und herunterladen. \item \textbf{Kein Schreibzugriff für Kunden:} Kunden können Dokumente nur einsehen und herunterladen.
\item \textbf{Kein internes Upload-UI in Houston:} Die Dokumentenverwaltung durch Mitarbeiter erfolgt ausschließlich über Filestash. \item \textbf{Kein internes Upload-UI in Houston:} Die Pflege durch Mitarbeiter erfolgt ausschließlich über Filestash.
\item \textbf{Keine freie Ordnerstruktur intern:} Nur die sieben definierten Typordner sind für Kunden sichtbar; weitere Ordner werden ignoriert. \item \textbf{Keine freie Ordnerstruktur intern:} Für Kunden sind nur die sieben Typordner sichtbar; weitere Ordner werden ignoriert.
\item \textbf{Keine Dashboard-Kacheln im PidI-Umfang:} Geplante Erweiterungen wie Dashboard-Kacheln für Tenant-Härtung aus Security-Assessment-Metadaten oder eine Anzeige zuletzt hinzugefügter Dokumente sind als Feature-Creep im Backlog erfasst, aber nicht Teil des PidI-Projekts. \item \textbf{Keine Dashboard-Kacheln im PidI-Umfang:} Erweiterungen wie Dashboard-Kacheln für Tenant-Härtung oder eine Anzeige zuletzt hinzugefügter Dokumente sind als Feature-Creep im Backlog erfasst, aber nicht Teil des PidI-Projekts.
\end{itemize} \end{itemize}
+5 -5
View File
@@ -10,12 +10,12 @@ Abbildung~\ref{fig:system-context} zeigt den Systemkontext des Dokumentenbereich
\label{fig:system-context} \label{fig:system-context}
\end{figure} \end{figure}
\textbf{Houston} ist das Kundenportal von WorkSimple, implementiert als ASP.NET-Core-Webanwendung mit Razor Pages. Es wurde im Rahmen dieses Projekts um den Dokumentenbereich erweitert. \textbf{Houston} ist das Kundenportal von WorkSimple, eine ASP.NET-Core-Webanwendung mit Razor Pages, die um den Dokumentenbereich erweitert wurde.
\textbf{Efecte} ist das ITSM-Tool des Unternehmens und liefert die eindeutige Organisations-ID jedes Kunden (\texttt{efecte-org-id}), die als Autorisierungsmerkmal für den S3-Kundenordner dient. \textbf{Efecte} ist das ITSM-Tool des Unternehmens und liefert die eindeutige Organisations-ID jedes Kunden (\texttt{efecte-org-id}) als Autorisierungsmerkmal für den S3-Kundenordner.
\textbf{Microsoft Entra~ID} (ehemals Azure~AD) übernimmt Authentifizierung und Autorisierung. Das Token enthält die Efecte-Organisations-ID und die Anwendungsrollen; die Rolle \texttt{Documents.Read} steuert den Zugriff auf den Dokumentenbereich. \textbf{Microsoft Entra~ID} (ehemals Azure~AD) übernimmt Authentifizierung und Autorisierung; das Token enthält die Efecte-Organisations-ID und die Anwendungsrollen, wobei \texttt{Documents.Read} den Zugriff steuert.
\textbf{S3-Speicher (NetApp StorageGRID)} ist die von Advanced Unibyte betriebene, S3-kompatible Ablage für Kundendokumente. Houston greift über das AWS SDK für .NET darauf zu. Jeder Kunde erhält einen eigenen Ordner, identifiziert über ein S3-Metadatum (\texttt{efecte-org-id}). \textbf{S3-Speicher (NetApp StorageGRID)} ist die von Advanced Unibyte betriebene, S3-kompatible Ablage für Kundendokumente, auf die Houston über das AWS SDK für .NET zugreift; jeder Kunde erhält einen eigenen, über \texttt{efecte-org-id} identifizierten Ordner.
\textbf{Filestash} ist ein webbasierter S3-Browser, den WorkSimple-Mitarbeiter intern zum Hochladen und Verwalten von Dokumenten nutzen. \textbf{Filestash} ist ein webbasierter S3-Browser für die interne Dokumentenpflege.
+2 -14
View File
@@ -1,18 +1,6 @@
\section{Zielsituation} \section{Zielsituation}
\label{sec:target-situation} \label{sec:target-situation}
Der Dokumentenbereich bietet folgende Funktionen: Der Dokumentenbereich stellt auf \texttt{/documents} alle Dokumente des Kunden als Liste dar, gegliedert nach sieben Dokumententypen; leere Ordner erscheinen nicht. Er bietet Typfilter und serverseitige Titelsuche, Paginierung, Einzel- und ZIP-Download, eine PDF-Vorschau im modalen Viewer sowie zeitlich begrenzte Share-Link-Previews. \texttt{.url}-Dateien verlinken beliebige Webadressen — etwa Teams-Kanäle oder SharePoint-Seiten — in der Dokumentenliste.
\begin{itemize} Die Mandantentrennung stellt sicher, dass jeder Kunde nur die Dokumente seines eigenen S3-Ordners sieht, identifiziert über die \texttt{efecte-org-id} aus dem Authentifizierungstoken; die Ordnerstruktur wird beim ersten Aufruf automatisch angelegt, sofern sie fehlt. Kunden haben ausschließlich lesenden Zugriff; die Pflege erfolgt durch Mitarbeiter über Filestash.
\item \textbf{Document Explorer:} Auf \texttt{/documents} werden alle Dokumente des Kunden als Liste dargestellt, gegliedert nach sieben Dokumententypen. Leere Ordner werden nicht angezeigt.
\item \textbf{Typfilter und Suche:} Filterung nach Typ und titelbasierte serverseitige Suche.
\item \textbf{Paginierung:} Seitenweise Darstellung großer Dokumentenmengen.
\item \textbf{Downloads:} Einzeldownload sowie ZIP-Download mehrerer ausgewählter Dokumente.
\item \textbf{PDF-Vorschau:} Anzeige von PDF-Dokumenten in einem modalen Viewer.
\item \textbf{Share-Link-Previews:} Erzeugung zeitlich begrenzter Freigabelinks.
\item \textbf{URL-Dateien:} Spezielle \texttt{.url}-Dateien ermöglichen die Verlinkung beliebiger Webadressen — z.\,B. Teams-Kanäle oder SharePoint-Seiten — in der Dokumentenliste.
\item \textbf{Mandantentrennung:} Jeder Kunde sieht ausschließlich die Dokumente in seinem eigenen S3-Ordner, der über die \texttt{efecte-org-id} aus dem Authentifizierungstoken identifiziert wird.
\item \textbf{Automatische Ordneranlage:} Beim ersten Aufruf der Dokumentenseite wird die vollständige Ordnerstruktur für den Kunden im S3 angelegt, sofern sie noch nicht existiert.
\end{itemize}
Kunden haben ausschließlich lesenden Zugriff; die Pflege erfolgt durch Mitarbeiter über Filestash.
+5 -13
View File
@@ -3,7 +3,7 @@
\subsection{Entwicklungsprozess} \subsection{Entwicklungsprozess}
Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps. Der dabei verbindliche Softwareprozess ist im internen XWiki dokumentiert und in Abbildung~\ref{fig:unicorn-process} wiedergegeben. Er beschreibt den vollständigen Weg von der Kundenidee bis zur Abrechnung und ordnet jedem Schritt den Zustand zu, den das zugehörige Work Item in Azure DevOps annimmt. Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps. Der verbindliche Softwareprozess — im internen XWiki dokumentiert und in Abbildung~\ref{fig:unicorn-process} wiedergegeben — beschreibt den Weg von der Kundenidee bis zur Abrechnung und ordnet jedem Schritt den Zustand des Work Items zu.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -12,20 +12,12 @@ Die Unicorn Development arbeitet agil mit zweiwöchigen Sprints in Azure DevOps.
\label{fig:unicorn-process} \label{fig:unicorn-process}
\end{figure} \end{figure}
Am Anfang steht eine Idee, die festgehalten und als Product Backlog Item ausformuliert wird; das Item steht dabei auf \texttt{New}. Nach der Prüfung der technischen Machbarkeit und der kaufmännischen Abwicklung — Angebot, Annahme, Auftragsbestätigung — geht es in die Freigabe (\texttt{To Approve}). Im Approval-Termin prüft das Team die \emph{Definition of Ready}, stellt Rückfragen und schätzt den Aufwand; danach gilt das Item als \texttt{Approved}. Mit der Sprintplanung wechselt es auf \texttt{Committed} und wird umgesetzt. Ist die Umsetzung abgeschlossen und in den Hauptbranch übernommen, steht es auf \texttt{Dev Completed}. Es folgen das Release ins Testsystem, das Review mit dem Kunden sowie die Tests durch Unicorn Development und Kunde; mit deren Abschluss erreicht das Item \texttt{Test Completed}. Erst danach erfolgt das Release ins Produktivsystem mit anschließender Abnahme; die abschließenden kaufmännischen Schritte — Rechnungsstellung, Abschluss in Kimai, Anpassung der Wartungsgebühr — überführen es nach \texttt{Done}. Das Dokumentenfeature durchlief den Prozess ohne Abweichung; da es eine Erweiterung des eigenen Produkts Houston ist, entfielen nur die auftragsbezogenen Schritte. Die Freigabestufe erwies sich als wirksam — Rückfragen im Approval-Termin, insbesondere durch \emph{Timo Walter}, deckten mehrfach Lücken in den Akzeptanzkriterien auf.
Das Dokumentenfeature wurde ohne Abweichung nach diesem Prozess bearbeitet. Da es sich um eine Erweiterung des eigenen Produkts Houston und nicht um einen Einzelauftrag handelt, entfielen lediglich die auftragsbezogenen Schritte; alle Freigabe-, Test- und Abnahmestufen wurden regulär durchlaufen.
Die Freigabestufe erwies sich dabei als wirksam: Rückfragen im Approval-Termin — insbesondere durch \emph{Timo Walter} — deckten mehrfach Lücken in den Akzeptanzkriterien auf, etwa zur Häufigkeit der Ordnerstrukturprüfung oder zu Namensbeschränkungen.
\subsection{Schnitt der Product Backlog Items} \subsection{Schnitt der Product Backlog Items}
Alle Arbeiten hängen als Product Backlog Items am Feature~484. Tabelle~\ref{tab:backlog} im Anhang listet sie mit Kennung, Titel, geschätztem Aufwand, Sprint und Zustand auf; Tabelle~\ref{tab:requirements} ordnet ihnen die Anforderungen aus Abschnitt~\ref{sec:requirements} zu. Alle Arbeiten hängen als Product Backlog Items am Feature~484. Tabelle~\ref{tab:backlog} im Anhang listet sie mit Kennung, Titel, Aufwand, Sprint und Zustand auf; Tabelle~\ref{tab:requirements} ordnet ihnen die Anforderungen aus Abschnitt~\ref{sec:requirements} zu.
Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 wurden acht Kern-Items angelegt, orientiert an der Feature-Beschreibung: Document Explorer als Basis, die \emph{Feature-Creep}-Punkte (Suche, Einzeldownload, ZIP-Download, PDF-Modal, URL-Dateien) als eigene Items. Am 7.~Juli kam die Typisierung über Icons hinzu, am 8.~Juli die automatische Ordneranlage. Der Backlog-Schnitt erfolgte in zwei Phasen. Am 18.~Juni 2026 wurden acht Kern-Items angelegt (Document Explorer als Basis, die \emph{Feature-Creep}-Punkte Suche, Einzeldownload, ZIP-Download, PDF-Modal und URL-Dateien als eigene Items); am 7.~Juli kam die Typisierung über Icons hinzu, am 8.~Juli die automatische Ordneranlage. Während der Umsetzung folgten am 22.~Juli Paginierung und Typfilter aus der UI-Zuarbeit, am 28.~Juli die Recherche zur S3-Abfrage (Ergebnis am 12.~August im Kundenordner-Lookup-Item) und am 17.~August das Item zu Race Conditions (siehe Abschnitt~\ref{sec:race-conditions}).
Eine zweite Gruppe entstand während der Umsetzung: Am 22.~Juli ergänzte die UI-Zuarbeit Paginierung und Typfilter. Am 28.~Juli wurde die Recherche zur Optimierung der S3-Abfrage angelegt, deren Ergebnis am 12.~August in das Kundenordner-Lookup-Item mündete. Am 17.~August kam das Item zu Race Conditions hinzu (siehe Abschnitt~\ref{sec:race-conditions}). Insgesamt sind es 15 Product Backlog Items und ein Bug; die Aufwände summieren sich auf 46 Story Points (35 in Sprint~15.2026, 11 in Sprint~16.2026). Alle vorgeschnittenen Items betreffen fachliche Funktionen, alle nachgeschobenen entstanden aus technischen Problemen — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
Insgesamt sind es 14 Product Backlog Items und ein Bug. Die geschätzten Aufwände summieren sich auf 46 Story Points: 35 in Sprint~15.2026, 11 in Sprint~16.2026.
Die vorgeschnittenen Items betreffen ausschließlich fachliche Funktionen; alle nachgeschobenen entstanden aus technischen Problemen bei der Implementierung — ein Muster, das in Abschnitt~\ref{sec:reflection} aufgegriffen wird.
+6 -6
View File
@@ -3,13 +3,13 @@
Das Feature~484 „Dokumente" existierte seit November 2023 als zweizeilige Bedarfsnotiz. Am 10.~Juni 2026 arbeitete \emph{Thomas Drewermann} es zu einer vollständigen Feature-Beschreibung aus — einschließlich S3, Filestash als Verwaltungswerkzeug, den sieben Dokumententypen sowie den Abschnitten \emph{Feature-Creep} und \emph{Out of Scope}. Das Feature~484 „Dokumente" existierte seit November 2023 als zweizeilige Bedarfsnotiz. Am 10.~Juni 2026 arbeitete \emph{Thomas Drewermann} es zu einer vollständigen Feature-Beschreibung aus — einschließlich S3, Filestash als Verwaltungswerkzeug, den sieben Dokumententypen sowie den Abschnitten \emph{Feature-Creep} und \emph{Out of Scope}.
Am 18.~Juni 2026 führte ich die Feature-Analyse durch. Vier Rückfragen wurden als Kommentare gestellt und am selben Tag beantwortet; drei entschieden Architekturfragen: Am 18.~Juni 2026 führte ich die Feature-Analyse durch; vier am selben Tag beantwortete Rückfragen entschieden Architekturfragen:
\begin{enumerate} \begin{enumerate}
\item \textbf{Autorisierung über Metadaten:} Die Efecte-Organisations-ID wird als Metadatum am Kundenordner gespeichert; die Berechtigung wird darüber aufgelöst (siehe Abschnitt~\ref{sec:authorization}). \item \textbf{Autorisierung über Metadaten:} Die Efecte-Organisations-ID wird als Metadatum am Kundenordner gespeichert und löst die Berechtigung auf (siehe Abschnitt~\ref{sec:authorization}).
\item \textbf{Filestash als externes Werkzeug:} Filestash dient unverändert als Verwaltungsoberfläche, \emph{nicht} als Referenz für einen Nachbau in Houston. \item \textbf{Filestash als externes Werkzeug:} Filestash dient als Verwaltungsoberfläche, \emph{nicht} als Vorlage für einen Nachbau in Houston.
\item \textbf{Typisierung über Ordner statt Metadaten:} Der Dokumententyp wird über den Unterordner bestimmt, nicht als Metafeld an der Datei. \item \textbf{Typisierung über Ordner statt Metadaten:} Der Dokumententyp wird über den Unterordner bestimmt.
\item \textbf{Bedeutung der URL-Dateien:} Gemeint ist das Einbetten \emph{externer} Ressourcen in den Dokumentenbereich, nicht das Teilen von Houston-Dokumenten nach außen. \item \textbf{Bedeutung der URL-Dateien:} Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen.
\end{enumerate} \end{enumerate}
Die dritte Entscheidung erwies sich als die folgenreichste: Die Typabbildung über Ordner macht die Filterung günstig (Präfix-Listing), erzwingt aber die automatische Anlage der Ordnerstruktur und erschwert die typübergreifende Suche. Die dritte Entscheidung war die folgenreichste: Die Typabbildung über Ordner macht die Filterung günstig (Präfix-Listing), erzwingt aber die automatische Ordneranlage und erschwert die typübergreifende Suche.
+4 -17
View File
@@ -1,7 +1,7 @@
\section{Beschaffung der S3-Infrastruktur} \section{Beschaffung der S3-Infrastruktur}
\label{sec:infrastructure} \label{sec:infrastructure}
Der S3-Speicher stand zu Projektbeginn nicht bereit und musste über den Service-Desk beantragt werden. Da Advanced Unibyte ihn betreibt, war ein mehrstufiger Abstimmungsweg nötig. Abbildung~\ref{fig:timeline-infra} zeigt den Verlauf. Der S3-Speicher stand zu Projektbeginn nicht bereit und musste über den Service-Desk beantragt werden. Da Advanced Unibyte ihn betreibt, war ein mehrstufiger Abstimmungsweg nötig; Abbildung~\ref{fig:timeline-infra} zeigt den Verlauf.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -12,23 +12,10 @@ Der S3-Speicher stand zu Projektbeginn nicht bereit und musste über den Service
\subsection{Bereitstellung der Buckets} \subsection{Bereitstellung der Buckets}
Am 22.~Juli 2026 stellte ich den Service Request. Er wurde am 23.~Juli \emph{Alexander Wagner} zugewiesen und am 27.~Juli abgeschlossen: drei Buckets (DEV, TEST, PROD) waren angelegt, die Zugangsdaten in Passbolt hinterlegt. Am 22.~Juli 2026 stellte ich den Service Request; er wurde am 23.~Juli \emph{Alexander Wagner} zugewiesen und am 27.~Juli abgeschlossen — drei Buckets (DEV, TEST, PROD) angelegt, Zugangsdaten in Passbolt hinterlegt. Da der Document Explorer ohnehin erst am 27.~Juli begann, entstand keine Verzögerung.
Da der Document Explorer ohnehin erst am 27.~Juli in die Umsetzung ging, entstand keine Verzögerung.
\subsection{Freischaltung zusätzlicher Funktionen} \subsection{Freischaltung zusätzlicher Funktionen}
Aufwendiger war die Klärung der verfügbaren S3-Funktionen. Im Rahmen der Lookup-Recherche (siehe Abschnitt~\ref{sec:lookup-research}) kamen zwei in Betracht: Im Rahmen der Lookup-Recherche (siehe Abschnitt~\ref{sec:lookup-research}) kamen zwei S3-Funktionen in Betracht: \textbf{S3 Select} (\texttt{SelectObjectContent}), das Objektinhalte serverseitig per SQL-ähnlicher Abfrage filtert \autocite{aws-s3-select, storagegrid-s3-select}, und der \textbf{Search Integration Service} von StorageGRID, der Objektmetadaten in einen Elasticsearch-Index spiegelt \autocite{storagegrid-search-integration}.
\begin{itemize} Am 30.~Juli beantragte ich beide Funktionen; \emph{Lennart Meinert} kontaktierte am 4.~August Advanced Unibyte, am 7.~August wurde S3 Select für alle drei Umgebungen aktiviert. Den Search Integration Service bot Advanced Unibyte laut Mitteilung vom 17.~August nicht an; da bereits eine Lösung ohne serverseitige Suche umgesetzt war (siehe Abschnitt~\ref{sec:lookup-decision}), wurde der Request geschlossen. Zwischen Antrag und Klärung lagen vier Wochen — ein Ansatz, der auf einer erst zu beschaffenden Fremdleistung beruht, ist in einem solchen Projektzeitraum nicht belastbar.
\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. \emph{Lennart Meinert} kontaktierte am 4.~August Advanced Unibyte; am 7.~August wurde S3 Select für alle drei Umgebungen aktiviert.
Für den Search Integration Service teilte Advanced Unibyte am 17.~August mit, dass die Funktion derzeit nicht angeboten werde, und schlug am 21.~August ein Folgegespräch vor. Da bereits eine Lösung ohne serverseitige Suche umgesetzt war (siehe Abschnitt~\ref{sec:lookup-decision}), wurde der Request geschlossen.
\subsection{Bewertung}
Zwischen erstem Antrag und abschließender Klärung lagen vier Wochen — zu Projektbeginn nicht eingeplant. Ein Lösungsansatz, der auf einer noch zu beschaffenden Fremdleistung beruht, ist innerhalb eines Projektzeitraums von wenigen Wochen nicht belastbar. Die gewählte Lösung kommt daher ohne Erweiterung der Speicherfunktionen aus.
+2 -6
View File
@@ -1,13 +1,9 @@
\section{Einarbeitung} \section{Einarbeitung}
\label{sec:onboarding} \label{sec:onboarding}
Vor der Implementierung war eine Einarbeitung in die projektrelevanten Technologien erforderlich, insbesondere S3.
\subsection{S3 als Objektspeicher} \subsection{S3 als Objektspeicher}
S3 (\emph{Simple Storage Service}) ist ein Objektspeicher, kein Dateisystem. Objekte werden über einen flachen Schlüsselraum adressiert; was als Ordner erscheint, ist ein Präfix im Objektschlüssel, das per \texttt{Delimiter} hierarchisch interpretiert wird \autocite{aws-listobjectsv2}. Diese Eigenschaft prägt das Ablagekonzept (siehe Abschnitt~\ref{sec:s3-layout}) und war für mehrere Architekturentscheidungen ausschlaggebend. S3 (\emph{Simple Storage Service}) ist ein Objektspeicher, kein Dateisystem: Objekte werden über einen flachen Schlüsselraum adressiert, und was als Ordner erscheint, ist ein per \texttt{Delimiter} hierarchisch interpretiertes Präfix \autocite{aws-listobjectsv2}. Das prägt das Ablagekonzept (siehe Abschnitt~\ref{sec:s3-layout}). S3 bietet zudem \emph{keine} Suche über benutzerdefinierte Metadaten; ein Lookup erfordert das Auflisten aller Objekte mit clientseitiger Filterung — das zentrale technische Problem des Projekts (siehe Abschnitt~\ref{sec:lookup-research}).
S3 bietet zudem \emph{keine} Suche über benutzerdefinierte Metadaten. Ein Lookup erfordert das Auflisten aller Objekte mit clientseitiger Filterung — das zentrale technische Problem des Projekts (siehe Abschnitt~\ref{sec:lookup-research}).
\subsection{AWS SDK für .NET} \subsection{AWS SDK für .NET}
@@ -15,4 +11,4 @@ Der Zugriff erfolgt über \texttt{IAmazonS3} aus dem AWS SDK für .NET (\texttt{
\subsection{NetApp StorageGRID} \subsection{NetApp StorageGRID}
Der Speicher wird von Advanced Unibyte auf Basis von NetApp StorageGRID betrieben. StorageGRID ist S3-kompatibel, implementiert die API jedoch nicht vollständig. Dies betraf im Projektverlauf \texttt{SelectObjectContent} \autocite{storagegrid-s3-select} und den \emph{Search Integration Service} \autocite{storagegrid-search-integration} und führte zu den in Abschnitt~\ref{sec:infrastructure} beschriebenen Abstimmungen. Der Speicher wird von Advanced Unibyte auf Basis von NetApp StorageGRID betrieben. StorageGRID ist S3-kompatibel, implementiert die API aber nicht vollständig; dies betraf \texttt{SelectObjectContent} \autocite{storagegrid-s3-select} und den \emph{Search Integration Service} \autocite{storagegrid-search-integration} (siehe Abschnitt~\ref{sec:infrastructure}).
+6 -6
View File
@@ -8,25 +8,25 @@ Aus Feature-Beschreibung und Analyse ergaben sich die folgenden Anforderungen (Z
\begin{itemize} \begin{itemize}
\item \textbf{FA-1 — Dokumentenliste:} Auf \texttt{/documents} werden alle Dokumente der Organisation aufgelistet; leere Typordner erscheinen nicht. \item \textbf{FA-1 — Dokumentenliste:} Auf \texttt{/documents} werden alle Dokumente der Organisation aufgelistet; leere Typordner erscheinen nicht.
\item \textbf{FA-2 — Mandantentrennung:} Ein Benutzer sieht ausschließlich Dokumente seiner Organisation, zugeordnet über die \texttt{efecte-org-id}. \item \textbf{FA-2 — Mandantentrennung:} Ein Benutzer sieht ausschließlich Dokumente seiner Organisation, zugeordnet über die \texttt{efecte-org-id}.
\item \textbf{FA-3 — Rollenbasierter Zugriff:} Der Navigationspunkt ist nur bei vorhandener Berechtigung sichtbar; Aufruf ohne Berechtigung ergibt 403. \item \textbf{FA-3 — Rollenbasierter Zugriff:} Der Navigationspunkt ist nur bei Berechtigung sichtbar; Aufruf ohne Berechtigung ergibt 403.
\item \textbf{FA-4 — Typisierung und Icons:} Jedes Dokument wird mit einem aus dem Unterordner abgeleiteten Icon dargestellt. \item \textbf{FA-4 — Typisierung und Icons:} Jedes Dokument erhält ein aus dem Unterordner abgeleitetes Icon.
\item \textbf{FA-5 — Suche:} Serverseitige Titelsuche mit Teiltreffern. \item \textbf{FA-5 — Suche:} Serverseitige Titelsuche mit Teiltreffern.
\item \textbf{FA-6 — Typfilter:} Filterelemente je Dokumententyp zum Ein- und Ausblenden. \item \textbf{FA-6 — Typfilter:} Filterelemente je Dokumententyp zum Ein- und Ausblenden.
\item \textbf{FA-7 — Paginierung:} Seitenweise Darstellung mit benutzerdefinierter Seitengröße, analog zu den übrigen Houston-Seiten. \item \textbf{FA-7 — Paginierung:} Seitenweise Darstellung mit benutzerdefinierter Seitengröße, analog zu den übrigen Houston-Seiten.
\item \textbf{FA-8 — Einzeldownload:} Download über eine zeitlich begrenzte Pre-Signed URL \autocite{aws-presigned-urls} unter Beibehaltung des Dateinamens. \item \textbf{FA-8 — Einzeldownload:} Download über eine zeitlich begrenzte Pre-Signed URL \autocite{aws-presigned-urls} unter Beibehaltung des Dateinamens.
\item \textbf{FA-9 — ZIP-Download:} Ausgewählte Dokumente werden als ZIP-Archiv heruntergeladen; die Ordnerstruktur entspricht der S3-Struktur. \item \textbf{FA-9 — ZIP-Download:} Ausgewählte Dokumente werden als ZIP-Archiv mit S3-analoger Ordnerstruktur heruntergeladen.
\item \textbf{FA-10 — PDF-Vorschau:} PDF-Anzeige im Modal ohne vorherigen Download. \item \textbf{FA-10 — PDF-Vorschau:} PDF-Anzeige im Modal ohne vorherigen Download.
\item \textbf{FA-11 — Share-Links:} Freigabelink mit OpenGraph-Meta-Tags für Linkvorschau und Weiterleitung auf den Document Explorer. \item \textbf{FA-11 — Share-Links:} Freigabelink mit OpenGraph-Meta-Tags für Linkvorschau und Weiterleitung auf den Document Explorer.
\item \textbf{FA-12 — URL-Dateien:} \texttt{.url}-Dateien werden nach INI-Format ausgewertet und leiten auf die hinterlegte Adresse weiter; fehlt eine gültige URL, wird die Datei normal behandelt. \item \textbf{FA-12 — URL-Dateien:} \texttt{.url}-Dateien werden nach INI-Format ausgewertet und leiten auf die hinterlegte Adresse weiter; fehlt eine gültige URL, wird die Datei normal behandelt.
\item \textbf{FA-13 — Automatische Ordneranlage:} Beim Aufruf wird die Typordnerstruktur für die Organisation angelegt, sofern sie fehlt. \item \textbf{FA-13 — Automatische Ordneranlage:} Beim Aufruf wird die Typordnerstruktur der Organisation angelegt, sofern sie fehlt.
\end{itemize} \end{itemize}
\subsection{Nichtfunktionale Anforderungen} \subsection{Nichtfunktionale Anforderungen}
\begin{itemize} \begin{itemize}
\item \textbf{NFA-1 — Skalierbarkeit des Lookups:} Die Auflösung des Kundenordners darf nicht linear mit der Kundenanzahl wachsen. \item \textbf{NFA-1 — Skalierbarkeit des Lookups:} Die Auflösung des Kundenordners darf nicht linear mit der Kundenanzahl wachsen.
\item \textbf{NFA-2 — Fehlerbehandlung:} Bei Ladefehlern wird eine verständliche Meldung angezeigt; eine stillschweigend leere Seite ist unzulässig. \item \textbf{NFA-2 — Fehlerbehandlung:} Bei Ladefehlern wird eine verständliche Meldung angezeigt, keine stillschweigend leere Seite.
\item \textbf{NFA-3 — Leerer Zustand:} Existieren keine Dokumente, wird ein Hinweis angezeigt. \item \textbf{NFA-3 — Leerer Zustand:} Ohne Dokumente wird ein Hinweis angezeigt.
\item \textbf{NFA-4 — Konsistenz:} Bedienelemente orientieren sich an den übrigen Houston-Seiten. \item \textbf{NFA-4 — Konsistenz:} Bedienelemente orientieren sich an den übrigen Houston-Seiten.
\item \textbf{NFA-5 — Testbarkeit:} Der Kundenordner-Lookup ist durch Unit-Tests mit gemocktem \texttt{IAmazonS3} abgedeckt. \item \textbf{NFA-5 — Testbarkeit:} Der Kundenordner-Lookup ist durch Unit-Tests mit gemocktem \texttt{IAmazonS3} abgedeckt.
\item \textbf{NFA-6 — Pfadsicherheit:} Beim Download wird geprüft, dass der Schlüssel innerhalb des Kundenordners liegt. \item \textbf{NFA-6 — Pfadsicherheit:} Beim Download wird geprüft, dass der Schlüssel innerhalb des Kundenordners liegt.
+42 -4
View File
@@ -1,7 +1,15 @@
\section{Zeit- und Aufwandsplanung} \section{Zeit- und Aufwandsplanung}
\label{sec:schedule} \label{sec:schedule}
Für das Praktikum sind 24 Manntage (192 Stunden) vorgesehen. Der Projektzeitraum reicht von Ende Mai 2026 bis zur Abgabe; die Umsetzung verteilt sich auf die Sprints 15–17.2026. Abbildung~\ref{fig:gantt} zeigt die Zeitplanung. Für das Praktikum sind 24 Manntage (192 Stunden) vorgesehen. Der \textbf{offizielle Tätigkeitszeitraum} des Praktikums ist der 07.07.--25.08.2026; in diesem Fenster wurden die 24 Manntage erbracht. Der fachliche Rahmen des Vorhabens ist jedoch weiter gefasst und in drei Abschnitte zu trennen:
\begin{itemize}
\item \textbf{Vorlauf (Ende Mai bis 06.07.2026):} Themenfindung, Ausarbeitung des Feature~484 durch den Fachbereich und die Feature-Analyse vom 18.~Juni (Abschnitt~\ref{sec:feature-analysis}). Diese Klärung fand im regulären Tagesgeschäft statt und zählt \emph{nicht} zu den 24 Manntagen.
\item \textbf{Offizieller Praxiseinsatz (07.07.--25.08.2026):} Anforderungen, Backlog-Schnitt, Umsetzung der Sprints 15 und 16.2026 sowie der Abnahmetest.
\item \textbf{Nachlauf (ab 26.08.2026):} Fehlerbehebung aus der Abnahme, Benutzerhandbuch und die Produktivsetzung am 03.~September. Diese Arbeiten fielen in Sprint 17.2026 und liegen ebenfalls außerhalb der 24 Manntage; sie werden dokumentiert, weil sie den Auslieferungsstand des Moduls belegen.
\end{itemize}
Abbildung~\ref{fig:gantt} zeigt die Zeitplanung über alle drei Abschnitte.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
@@ -10,8 +18,38 @@ Für das Praktikum sind 24 Manntage (192 Stunden) vorgesehen. Der Projektzeitrau
\label{fig:gantt} \label{fig:gantt}
\end{figure} \end{figure}
Vier Phasen gliedern die Planung: Die \textbf{Analyse- und Konzeptionsphase} (Ende Mai bis Anfang Juli) umfasste Themenfindung, Feature-Analyse und Backlog-Schnitt. Die \textbf{Implementierungsphase} begann am 27.~Juli über die Sprints 15 und 16. Parallel lief die \textbf{Qualitätssicherung} (Code-Reviews, Abnahmetest ab 13.~August). Die \textbf{Abschlussphase} umfasst Release, Dokumentation und Präsentation. Vier Phasen gliedern die Planung: die \textbf{Analyse- und Konzeptionsphase} (Ende Mai bis Anfang Juli, überwiegend im Vorlauf) mit Themenfindung, Feature-Analyse und Backlog-Schnitt; die \textbf{Implementierungsphase} ab 27.~Juli über die Sprints 15 und 16; die parallele \textbf{Qualitätssicherung} (Code-Reviews, Abnahmetest ab 13.~August); und die \textbf{Abschlussphase} in Sprint 17.2026 mit Fehlerbehebung, Benutzerdokumentation und Produktivsetzung am 3.~September. Die Planung hing von der Infrastrukturbereitstellung ab — der Document Explorer konnte erst nach Verfügbarkeit des S3-Speichers umgesetzt werden; \emph{Stephan Janßen} vermerkte diese Abhängigkeit am 8.~Juli am Item.
Die Planung hing von der Infrastrukturbereitstellung ab: Der Document Explorer konnte erst nach Verfügbarkeit des S3-Speichers umgesetzt werden. \emph{Stephan Janßen} vermerkte diese Abhängigkeit am 8.~Juli als Voraussetzung am Item.
Der Soll-Ist-Vergleich der Zeitplanung findet sich in Abschnitt~\ref{sec:target-comparison}. Der Soll-Ist-Vergleich der Zeitplanung findet sich in Abschnitt~\ref{sec:target-comparison}.
\subsection{Verteilung der Manntage}
\label{sec:effort-distribution}
Der Praxiseinsatz war kein Vollzeitprojekt: Innerhalb des Tätigkeitszeitraums war ich parallel in das reguläre Tagesgeschäft eingebunden — Wartung bestehender Houston-Module, Support-Tickets, Code-Reviews fremder Pull Requests sowie Sprint-Zeremonien. Die 24 Manntage verteilen sich daher ungleichmäßig über die 36 Arbeitstage des Zeitraums. Tabelle~\ref{tab:manntage} weist die kalenderwöchentliche Verteilung aus.
\begin{longtable}{@{} l p{28mm} c c p{45mm} @{}}
\caption{Verteilung der 24 Manntage über den Tätigkeitszeitraum}
\label{tab:manntage} \\
\toprule
\textbf{KW} & \textbf{Zeitraum} & \textbf{AT} & \textbf{MT} & \textbf{Schwerpunkt im Projekt} \\
\midrule
\endfirsthead
\toprule
\textbf{KW} & \textbf{Zeitraum} & \textbf{AT} & \textbf{MT} & \textbf{Schwerpunkt im Projekt} \\
\midrule
\endhead
\bottomrule
\endfoot
28 & 07.--10.07. & 4 & 3 & Anforderungen, Backlog-Schnitt \\
29 & 13.--17.07. & 5 & 2 & Verfeinerung der Backlog Items \\
30 & 20.--24.07. & 5 & 2 & Infrastrukturbeantragung, Einarbeitung S3 \\
31 & 27.--31.07. & 5 & 4 & Document Explorer, S3-Client \\
32 & 03.--07.08. & 5 & 4 & Typ-Icons, Suche \\
33 & 10.--14.08. & 5 & 3 & Downloads, PDF-Vorschau, Abnahmebeginn \\
34 & 17.--21.08. & 5 & 4 & Typfilter, Paginierung, Ordner-Lookup \\
35 & 24.--25.08. & 2 & 2 & Abnahmebegleitung, Code-Reviews \\
\midrule
\textbf{Summe} & 07.07.--25.08. & \textbf{36} & \textbf{24} & \\
\end{longtable}
Die geringere Belastung in den Kalenderwochen 29 und 30 hat zwei Gründe: Zum einen war der S3-Speicher bis zum 27.~Juli nicht verfügbar, sodass die Implementierung nicht beginnen konnte (Abschnitt~\ref{sec:infrastructure}); zum anderen liefen in diesen Wochen Aufgaben aus anderen Houston-Modulen weiter. Die verlorene Kapazität wurde in den Kalenderwochen 31, 32 und 34 aufgeholt, in denen das Projekt nahezu die gesamte Arbeitszeit beanspruchte.
+6 -26
View File
@@ -3,36 +3,16 @@
\subsection{Ablauf} \subsection{Ablauf}
Die fachliche Abnahme übernahm \emph{Maria-Lena Andersz}. Sie begann am 13.~August 2026 und zog sich mit Unterbrechungen bis zum 25.~August; geprüft wurde gegen die Akzeptanzkriterien der Backlog Items auf der Testumgebung. Die fachliche Abnahme übernahm \emph{Maria-Lena Andersz}. Sie begann am 13.~August 2026 und zog sich mit Unterbrechungen bis zum 25.~August; geprüft wurde gegen die Akzeptanzkriterien der Backlog Items auf der Testumgebung. Die Abnahme zerfiel in zwei Abschnitte: zunächst Zugriffs- und Konfigurationsprobleme, erst danach die funktionale Prüfung.
Die Abnahme zerfiel in zwei Abschnitte: zunächst Zugriffs- und Konfigurationsprobleme, erst danach die funktionale Prüfung. \subsection{Zugriff und Konfiguration}
\subsection{Der Menüpunkt war nicht sichtbar} Der Menüpunkt „Dokumente" erschien nicht — Ursache war nicht der Code, sondern die Berechtigungsvergabe: der Testerin war die Anwendungsrolle nicht zugewiesen, das Verhalten entsprach der Spezifikation (Abschnitt~\ref{sec:authorization}). Die Rolle war Ende Juli nur für die Entwicklungsumgebung beantragt worden; dass ihre Vergabe an Testende ein eigener Schritt ist, war nirgends festgehalten. Hier bewährte sich die 403-Antwort: Bei 404 wäre nicht unterscheidbar gewesen, ob die Seite fehlt oder die Berechtigung.
Der Menüpunkt „Dokumente" erschien nicht. Die Ursache lag nicht im Code, sondern in der Berechtigungsvergabe — der Testerin war die Anwendungsrolle nicht zugewiesen. Das Verhalten entsprach der Spezifikation: Ohne Rolle ist der Menüpunkt unsichtbar (siehe Abschnitt~\ref{sec:authorization}). Nach Klärung wurden keine Dokumente angezeigt; auch das lag an der Umgebung: fehlende Testdateien und eine falsche Zuordnung zwischen Testkonto und Organisation. Ob ein Benutzer Dokumente sieht, hängt von einer Kette ab — Anmeldetoken, Organisationszuordnung, Metadatum am Kundenordner; ist ein Glied falsch, zeigt die Seite keine Dokumente, aber auch keinen Fehler. Eine Prüfliste für die Inbetriebnahme hätte diesen Abschnitt verkürzt.
Der Vorgang legt eine Lücke offen: Die Rolle war Ende Juli für die Entwicklungsumgebung beantragt worden; dass ihre Vergabe an Testende ein eigener Schritt ist, war weder in Akzeptanzkriterien noch Übergabe festgehalten. Eine rollenbasierte Funktion bringt Anforderungen mit, die über den Code hinausgehen.
Hier bewährte sich die 403-Antwort (Abschnitt~\ref{sec:authorization}): Bei 404 wäre für die Testerin nicht unterscheidbar gewesen, ob die Seite fehlt oder die Berechtigung.
\subsection{Konfiguration und Testdaten}
Nach Klärung der Berechtigung wurden keine Dokumente angezeigt. Auch hier lag die Ursache in der Umgebung: Es fehlten Testdateien im Speicher, und die Zuordnung zwischen Testkonto und Organisation war nicht korrekt konfiguriert.
Das ist charakteristisch für das Ablagekonzept: Ob ein Benutzer Dokumente sieht, hängt von einer Kette ab — Anmeldetoken, Organisationszuordnung, Metadatum am Kundenordner. Ist ein Glied falsch, zeigt die Seite keine Dokumente, aber auch keinen Fehler. Nach Bereinigung funktionierte die Anzeige.
Eine Prüfliste für die Inbetriebnahme — Speicher erreichbar, Testdaten vorhanden, Rolle vergeben, Organisationszuordnung gesetzt — hätte diesen Abschnitt verkürzt.
\subsection{Funktionale Prüfung} \subsection{Funktionale Prüfung}
Am 24.~August fand die funktionale Abnahme statt: Paginierung, Suchverhalten, Sprungmarken der Freigabelinks, Fehlerverhalten, Verknüpfungsdateien und Download. Am 24.~August fand die funktionale Abnahme statt: Paginierung, Suchverhalten, Sprungmarken der Freigabelinks, Fehlerverhalten, Verknüpfungsdateien und Download. Sie verlief überwiegend erfolgreich; einzelne Bestandteile waren noch nicht verfügbar, da der Pull Request zur Ordnerverwaltung erst am 26.~August zusammengeführt wurde (Abschnitt~\ref{sec:folder-management}).
Die Prüfung verlief überwiegend erfolgreich; einzelne Bestandteile waren nicht verfügbar, da der letzte Pull Request offen war (siehe Abschnitt~\ref{sec:folder-management}). Abweichungen wurden als Fehlerberichte erfasst. Ein Fehlerbericht: Das Suchfeld blendet nach Eingabe eine Schaltfläche zum Leeren ein, die auf den übrigen Houston-Seiten nicht existiert und damit NFA-4 (Konsistenz) verletzt. Weder Unit-Test noch Code-Review hätten ihn finden können; sichtbar wird die Abweichung erst beim Vergleich mit den bestehenden Seiten. Die Behebung — Entfernen der Schaltfläche — wurde am 27.~August über Pull Request 2206 zusammengeführt. Damit war der Dokumentenbereich vollständig abgenommen; sämtliche Backlog Items standen auf \texttt{Test Completed}.
\subsection{Der gefundene Fehler}
Ein Fehlerbericht: Das Suchfeld blendet nach Eingabe eine Schaltfläche zum Leeren ein, die auf den übrigen Houston-Seiten nicht existiert — eine Abweichung in Darstellung und Verhalten.
Die Schaltfläche ist eine sinnvolle Erleichterung; der Fehler liegt darin, dass sie nur an dieser Stelle existiert, was NFA-4 (Konsistenz) verletzt.
Weder Unit-Test noch Code-Review hätten ihn finden können; sichtbar wird die Abweichung erst beim Vergleich mit den bestehenden Seiten — das leistet nur ein Abnahmetest durch eine Person, die die Anwendung kennt. Der Fehler wurde für den folgenden Sprint eingeplant.
+5 -29
View File
@@ -1,42 +1,18 @@
\section{Code-Reviews} \section{Code-Reviews}
\label{sec:code-reviews} \label{sec:code-reviews}
\subsection{Umfang} \subsection{Umfang und Prüftiefe}
Die Umsetzung verteilte sich auf elf Pull Requests mit 113 Diskussionssträngen (Tabelle~\ref{tab:pull-requests} im Anhang). Kein Pull Request wurde abgelehnt; sämtliche erhielten eine Freigabe. Die inhaltliche Auseinandersetzung fand durchgängig in den Diskussionssträngen statt. Die Umsetzung verteilte sich auf dreizehn Pull Requests mit 114 Diskussionssträngen (Tabelle~\ref{tab:pull-requests} im Anhang). Kein Pull Request wurde abgelehnt; dass keiner verworfen wurde, geht auf die vorgelagerte Klärung durch Feature-Analyse und Backlog-Durchsicht zurück. Die Verteilung der Stränge folgte dem Risiko: die intensivste Prüfung erfuhren Einzeldownload (30 Stränge) und Freigabelinks (23), gefolgt vom Document Explorer (21); die PDF-Vorschau als reine Darstellungsfunktion kam mit zwei aus.
Dass kein Pull Request verworfen wurde, ist kein Zufall: Die vorgelagerte Klärung — Feature-Analyse im Juni, Backlog-Durchsicht im Juli — hatte die fachlichen Fragen so weit beantwortet, dass keine Implementierung auf einer falschen Annahme beruhte.
\subsection{Prüftiefe und Risiko}
Die Verteilung der Diskussionsstränge folgt dem Risiko: Die intensivste Prüfung erfuhren Einzeldownload (30 Stränge) und Freigabelinks (23) — die Arbeiten, bei denen ein Fehler Kundendokumente offengelegt hätte. Der Document Explorer folgt mit 21 Strängen, die PDF-Vorschau mit zwei: eine Darstellungsfunktion ohne eigene Sicherheitsentscheidung.
Die Verteilung entstand ohne Vorgabe — die Reviewer lenkten ihre Aufmerksamkeit intuitiv dorthin, wo ein Fehler teuer gewesen wäre.
\subsection{Wiederkehrende Themen} \subsection{Wiederkehrende Themen}
Über alle Diskussionsstränge hinweg lassen sich fünf Muster erkennen. Über alle Diskussionsstränge hinweg lassen sich fünf Muster erkennen. \textbf{Sicherheit und Eingabeprüfung} machte den größten Anteil aus: Pfadprüfung beim Download, zeitlich begrenzte Zugriffs-URLs statt Dateiströme, Ausschluss benutzerdefinierter Symbole aus der Vorschau und keine Auslieferung über nicht authentifizierte Pfade. \textbf{Kompatibilität vor Ideallösung}: Mehrfach wurde eine sauberere Lösung zugunsten einer verlässlicheren verworfen — etwa Vorschaubilder als Rastergrafik, weil die Vektorvariante von verbreiteten Messengern nicht zuverlässig dargestellt wird (Abschnitt~\ref{sec:pdf-preview}). \textbf{Abgrenzung statt Ausweitung}: Erkannte Probleme wurden als neue Backlog Items erfasst statt im laufenden Pull Request miterledigt. \textbf{Struktur und Wartbarkeit}: auszulagernde Skripte, fehlertolerantes Auswerten von Aufzählungswerten, überflüssige Kommentare, uneinheitliche Benennungen. \textbf{Fachliche Klärung im Review}: In mehreren Fällen änderte die Diskussion nicht den Code, sondern das Backlog Item — etwa den Statuscode bei fehlender Berechtigung (403 statt 404).
\textbf{Sicherheit und Eingabeprüfung.} Der größte Anteil entfiel auf die Prüfung externer Angaben: Pfadprüfung beim Download, zeitlich begrenzte Zugriffs-URLs statt Dateiströme, Ausschluss benutzerdefinierter Symbole aus der Vorschau und der Grundsatz, keine Inhalte über nicht authentifizierte Pfade auszuliefern.
\textbf{Kompatibilität vor Ideallösung.} Mehrfach wurde eine technisch sauberere Lösung zugunsten einer verlässlicheren verworfen — etwa die Bereitstellung der Vorschaubilder als Rastergrafik, weil die Vektorvariante von verbreiteten Messengern nicht zuverlässig dargestellt wird (siehe Abschnitt~\ref{sec:pdf-preview}).
\textbf{Abgrenzung statt Ausweitung.} Erkannte Probleme wurden als neue Backlog Items erfasst statt im laufenden Pull Request miterledigt — etwa Speicherabfrage, Typsuche und Nebenläufigkeit. Das hielt die Pull Requests begrenzt und machte die Probleme sichtbar.
\textbf{Struktur und Wartbarkeit.} Wiederkehrend: auszulagernde Skripte, fehlertolerantes Auswerten von Aufzählungswerten, überflüssige Kommentare, uneinheitliche Benennungen.
\textbf{Fachliche Klärung im Review.} In mehreren Fällen änderte die Diskussion nicht den Code, sondern das Backlog Item — etwa den Statuscode bei fehlender Berechtigung (403 statt 404) und die Festlegung, dass auch einzelne Dokumente als Archiv ausgeliefert werden. Das Review leistete damit Anforderungsarbeit.
\subsection{Wechsel der Reviewer} \subsection{Wechsel der Reviewer}
Über die Projektlaufzeit wechselte der Hauptreviewer zweimal: \emph{Timo Walter} prüfte die fünf Pull Requests der Anfangsphase, \emph{Sarah Hinzmann} übernahm Anfang August, \emph{Robin Noack} die Schlussphase. Zusätzlich beteiligte sich \emph{Hanna Ebner} an den frühen Diskussionen. Der Hauptreviewer wechselte zweimal: \emph{Timo Walter} prüfte die fünf Pull Requests der Anfangsphase, \emph{Sarah Hinzmann} übernahm Anfang August, \emph{Robin Noack} die Schlussphase; \emph{Hanna Ebner} beteiligte sich an den frühen Diskussionen. Der Wechsel hatte einen fachlichen Effekt: frühe Reviews befassten sich mit Architektur und Sicherheit, späte mit Benennung und Codestil; zugleich verteilte er das Wissen über das Modul im Team.
Der Wechsel war organisatorisch bedingt, hatte aber einen fachlichen Effekt: Frühe Reviews befassten sich mit Architektur, Sicherheit und Speicherabfrage; mittlere mit Bedienung und Wartbarkeit; späte mit Benennung, Fehlertoleranz und Codestil. Ein gleicher Reviewer hätte diese Bandbreite vermutlich nicht abgedeckt; zugleich verteilte der Wechsel das Wissen über das Modul im Team.
\subsection{Kritische Betrachtung des Vorgehens} \subsection{Kritische Betrachtung des Vorgehens}
Am 27.~Juli wurden vier Pull Requests am selben Tag eröffnet, ein fünfter folgte am Tag darauf. Da sie inhaltlich aufeinander aufbauten, ließ sich nur der erste zeitnah abschließen; die übrigen blieben zwei bis drei Wochen offen. Am 27.~Juli wurden vier Pull Requests am selben Tag eröffnet, ein fünfter folgte tags darauf. Da sie inhaltlich aufeinander aufbauten (Abschnitt~\ref{sec:architecture}), ließ sich nur der erste zeitnah abschließen; die übrigen blieben zwei bis drei Wochen offen, und der überwiegende Teil der Zusammenführungen fiel in die Woche vom 18.~bis 21.~August — unmittelbar vor der Abnahme. Sequenzielles Vorgehen wäre vorzuziehen gewesen, da die inhaltliche Abhängigkeit echtes paralleles Vorankommen ohnehin verhinderte.
Die in Abschnitt~\ref{sec:architecture} beschriebene Verkettung machte die Änderungen gut prüfbar, verlagerte den Aufwand aber ans Ende: Der überwiegende Teil der Zusammenführungen fällt in die Woche vom 18. bis 21.~August — unmittelbar vor Abnahme und Produktivsetzung.
Rückblickend wäre sequenzielles Vorgehen vorzuziehen gewesen, da die inhaltliche Abhängigkeit echtes paralleles Vorankommen ohnehin verhinderte.
+8 -16
View File
@@ -1,26 +1,18 @@
\section{Auslieferung} \section{Auslieferung}
\label{sec:releases} \label{sec:releases}
\subsection{Umgebungen} \subsection{Umgebungen und Rollen}
Die Auslieferung folgt dem in Houston etablierten dreistufigen Weg über Entwicklungs-, Test- und Produktivumgebung. Jede Stufe besitzt einen eigenen Speicher (siehe Abschnitt~\ref{sec:s3-client}); eine Auslieferung setzt voraus, dass dieser eingerichtet, erreichbar und korrekt hinterlegt ist. Die Auslieferung folgt dem in Houston etablierten dreistufigen Weg über Entwicklungs-, Test- und Produktivumgebung. Jede Stufe besitzt einen eigenen Speicher (Abschnitt~\ref{sec:s3-client}), der eingerichtet, erreichbar und korrekt hinterlegt sein muss; deshalb wurde die Infrastrukturbereitstellung (Abschnitt~\ref{sec:infrastructure}) bereits im Approval-Termin als Voraussetzung vermerkt.
Deshalb wurde die Infrastrukturbereitstellung (Abschnitt~\ref{sec:infrastructure}) bereits im Approval-Termin als Voraussetzung vermerkt. Auch die Anwendungsrolle muss je Umgebung vorhanden und zugewiesen sein — eine begleitende Maßnahme, die im Abnahmetest zu Verzögerungen führte (Abschnitt~\ref{sec:acceptance-testing}). Vor der Produktivsetzung wurde daher am 20.~August geprüft, ob sämtliche Einstiegspunkte des Moduls hinter der Rolle liegen; die zentrale Registrierung (Abschnitt~\ref{sec:architecture}) macht eine ungeschützte Route unwahrscheinlich, die Prüfung bestätigte dies.
\subsection{Rollen als Teil der Auslieferung} \subsection{Benutzerdokumentation}
Die Anwendungsrolle muss je Umgebung vorhanden sein und den betreffenden Personen zugewiesen werden — kein Bestandteil des Anwendungsstands, sondern eine begleitende Maßnahme. Zum Funktionsumfang gehört das als PDF ausgelieferte Houston-Benutzerhandbuch, das für den Dokumentenbereich um ein eigenes Kapitel ergänzt wurde: Aufruf der Seite, Typfilter und Suche, Einzel- und Archivdownload, PDF-Vorschau, Freigabelinks sowie Verknüpfungsdateien. Erfasst als eigenes Backlog Item (10167) und über Pull Request 2215 am 2.~September zusammengeführt. Die Trennung von der Implementierung ist bewusst: Das Handbuch beschreibt den Endzustand und ließ sich sinnvoll erst schreiben, als sämtliche Funktionen zusammengeführt waren.
Dieser Punkt führte im Abnahmetest zu Verzögerungen (Abschnitt~\ref{sec:acceptance-testing}). Vor der Produktivsetzung wurde daher am 20.~August geprüft, ob sämtliche Funktionen des Moduls hinter der Rolle liegen. \subsection{Stand bei Produktivsetzung}
Die Prüfung ist nötig, weil das Modul mehrere Einstiegspunkte besitzt: Wäre eine Route nicht von der zentralen Richtlinie erfasst, bliebe sie ohne Anmeldung erreichbar, ohne dass dies sichtbar wäre. Die zentrale Registrierung (Abschnitt~\ref{sec:architecture}) macht diesen Fehler unwahrscheinlich; die Prüfung bestätigt dies. Alle dreizehn Pull Requests des Features sind zusammengeführt. Die Kernfunktionen entstanden in der Woche vom 18.~bis 21.~August; die Ordnerverwaltung mit dem neuen Kundenordner-Lookup folgte am 26.~August (Pull Request 2196), der Fehler aus der Abnahme wurde am 27.~August behoben (Pull Request 2206). Sämtliche Backlog Items stehen im Zustand \texttt{Test Completed} oder \texttt{Done}, die Recherche zur Speicherabfrage (9857) ist abgeschlossen. Am 3.~September wurde der Dokumentenbereich in die Produktivumgebung ausgeliefert.
\subsection{Stand bei Abgabe} Offen bleibt einzig das Backlog Item zur Nebenläufigkeit im verändernden Lookup-Pfad (10070). Es ist freigegeben, aber nicht eingeplant, da die Gegenmaßnahme von einer Rückfrage beim Betreiber des Speichers abhängt (Abschnitt~\ref{sec:race-conditions}).
Bei Abgabe waren zehn der elf Pull Requests zusammengeführt, überwiegend in der Woche vom 18.~bis 21.~August. Der Dokumentenbereich war damit in seinen Kernfunktionen — Liste, Typisierung, Suche, Filter, Paginierung, Downloads, Vorschau, Freigabelinks und Verknüpfungsdateien — ausgeliefert.
Nicht abgeschlossen war die Ordnerverwaltung mit dem neuen Kundenordner-Lookup. Der Pull Request war fachlich fertig, aber nicht freigegeben; bis dahin bleibt das in Abschnitt~\ref{sec:document-explorer} beschriebene lineare Verfahren — funktional korrekt, aber mit bekannter Skalierungsschwäche.
Ebenfalls offen sind das Backlog Item zur Nebenläufigkeit sowie der Fehlerbericht aus der Abnahme, der für den folgenden Sprint eingeplant ist.
Der Dokumentenbereich ist in Betrieb, aber nicht vollständig abgeschlossen (Abschnitt~\ref{sec:target-comparison}).
+2 -10
View File
@@ -1,14 +1,6 @@
\section{Teststrategie} \section{Teststrategie}
\label{sec:test-strategy} \label{sec:test-strategy}
Die Qualitätssicherung stützte sich auf drei Stufen. Die Qualitätssicherung stützte sich auf drei Stufen. \textbf{Unit-Tests} sichern isoliert prüfbare Logik ab — vor allem Kundenordner-Auflösung und Pfadbehandlung — und laufen bei jedem Übersetzungsvorgang. \textbf{Code-Reviews} prüfen Entwurf, Lesbarkeit und Sicherheitseigenschaften, etwa ob eine Information auf einem nicht authentifizierten Pfad ausgeliefert werden darf. \textbf{Abnahmetests} prüfen gegen die Akzeptanzkriterien in der tatsächlichen Umgebung und erfassen Fehler aus dem Zusammenspiel von Konfiguration, Berechtigungen und realen Daten.
\textbf{Unit-Tests} sichern isoliert prüfbare Logik ab — hier vor allem Kundenordner-Auflösung und Pfadbehandlung. Sie laufen bei jedem Übersetzungsvorgang. Die automatisierten Tests folgten dem Risiko, nicht einer Abdeckungsvorgabe: Für den Kundenordner-Lookup war Testabdeckung ausdrücklich als Akzeptanzkriterium aufgenommen, für die PDF-Vorschau nicht — ein Fehler im Lookup führt zur Anzeige fremder Dokumente oder Datenverlust und ist im Betrieb schwer zu bemerken, ein Fehler in der Vorschau ist beim ersten Aufruf offensichtlich. Die Kehrseite: Fehler in Bereichen ohne automatisierte Abdeckung wurden erst im Abnahmetest gefunden (Abschnitt~\ref{sec:acceptance-testing}).
\textbf{Code-Reviews} prüfen Entwurf, Lesbarkeit und Sicherheitseigenschaften — Fehlerarten, für die kein automatisierter Test formulierbar ist, etwa ob eine Information auf einem nicht authentifizierten Pfad ausgeliefert werden darf.
\textbf{Abnahmetests} prüfen gegen die Akzeptanzkriterien in der tatsächlichen Umgebung. Sie erfassen Fehler aus dem Zusammenspiel von Konfiguration, Berechtigungen und realen Daten — eine Klasse, die in Unit-Tests und Reviews unsichtbar bleibt.
Die automatisierten Tests folgten dem Risiko, nicht einer Abdeckungsvorgabe. Für den Kundenordner-Lookup war Testabdeckung ausdrücklich als Akzeptanzkriterium aufgenommen; für die PDF-Vorschau nicht. Ein Fehler im Lookup führt zur Anzeige fremder Dokumente oder Datenverlust und ist im Betrieb schwer zu bemerken; ein Fehler in der Vorschau ist beim ersten Aufruf offensichtlich.
Diese Priorisierung hat eine Kehrseite: Fehler in Bereichen ohne automatisierte Abdeckung wurden erst im Abnahmetest gefunden (Abschnitt~\ref{sec:acceptance-testing}).
+4 -18
View File
@@ -3,26 +3,12 @@
\subsection{Testbarkeit durch Kapselung} \subsection{Testbarkeit durch Kapselung}
Da der Speicherzugriff über die SDK-Schnittstelle erfolgt, lässt sich diese in Tests durch eine Attrappe ersetzen. Die Tests laufen damit ohne Netzwerkverbindung, ohne Zugangsdaten und ohne einen realen Speicher. Da der Speicherzugriff über die SDK-Schnittstelle erfolgt, lässt sich diese in Tests durch eine Attrappe ersetzen; die Tests laufen ohne Netzwerkverbindung, Zugangsdaten und realen Speicher. Die Attrappe erlaubt zudem Zustände, die sich real kaum erzeugen ließen — etwa einen Kundenordner mit falschem Namen, aber korrektem Metadatum, oder einen Ordner ohne Metadatum.
Die Attrappe erlaubt Zustände, die sich real kaum erzeugen ließen — etwa einen Kundenordner mit falschem Namen, aber korrektem Metadatum, oder einen Ordner ohne Metadatum. Gerade diese Randfälle könnten ohne Test unbemerkt fehlerhaft bleiben.
\subsection{Abgedeckte Pfade} \subsection{Abgedeckte Pfade}
Die Tests bilden die in Abschnitt~\ref{sec:lookup-decision} beschriebenen Wege einzeln ab: Direktzugriff, Kollisionsfall, Rückfallebene mit Umbenennung, Anlegen eines fehlenden Kundenordners und Ignorieren eines Ordners ohne gültiges Metadatum. Geprüft wird nicht nur das Ergebnis, sondern auch die Speicherzugriffe — beim Direktzugriff etwa, dass genau eine Metadatenabfrage und keine Auflistung stattfand. Die Tests bilden die in Abschnitt~\ref{sec:lookup-decision} beschriebenen Wege einzeln ab: Direktzugriff, Kollisionsfall, Rückfallebene mit Umbenennung, Anlegen eines fehlenden Kundenordners und Ignorieren eines Ordners ohne gültiges Metadatum. Geprüft wird nicht nur das Ergebnis, sondern auch die Speicherzugriffe — beim Direktzugriff etwa, dass genau eine Metadatenabfrage und keine Auflistung stattfand. Denn die Anforderung war keine funktionale, sondern eine über den Aufwand: dass der Regelfall mit einem einzigen Zugriff auskommt, lässt sich nur über die beobachteten Aufrufe prüfen.
Die Anforderung an den Lookup war keine funktionale, sondern eine über den Aufwand. Ein Test, der nur das richtige Präfix prüft, bestünde auch, wenn die Implementierung weiterhin alle Ordner durchliefe. Die eigentliche Eigenschaft — dass der Regelfall mit einem einzigen Zugriff auskommt — lässt sich nur über die beobachteten Aufrufe prüfen. \subsection{Befunde aus dem Review von Testcode}
\subsection{Testdaten} Zwei Reviewhinweise \emph{Timo Walters} betrafen die Tests selbst. Zum einen verwendeten Testdaten reale Kundennamen statt Platzhaltern; die Daten wurden ersetzt. Zum anderen bestand ein Test aus dem falschen Grund: Er brach bereits an einem unbekannten Typbezeichner ab, ohne die zu prüfende Sicherheitseigenschaft — dass ein in die Adresse geschriebener Kundenname kein Ergebnis liefert — je zu erreichen. Der Fund war nur durch Lesen zu erzielen, da der Test grün war. Als Konsequenz wurde die Anforderung, dass ein Test ohne den jeweiligen Fix fehlschlagen muss, in die Akzeptanzkriterien des Folgeitems zur Nebenläufigkeit aufgenommen (Abschnitt~\ref{sec:race-conditions}).
\emph{Timo Walter} merkte an, dass reale Kundennamen in den Tests verwendet wurden, und schlug Platzhalter vor. Der Hinweis ist klein, in der Sache aber richtig: Testdaten liegen in der Versionsverwaltung und bleiben dauerhaft lesbar; ein realer Kundenname ist eine unnötige Offenlegung, da der Test mit Platzhaltern denselben Zweck erfüllt. Die Daten wurden ersetzt.
\subsection{Ein Test, der das Falsche prüfte}
\emph{Timo Walter} bemerkte beim Lesen, dass ein Test bestand, aber aus dem falschen Grund: Der Prüfling brach in der ersten Zeile ab, weil die Pfadauswertung einen unbekannten Typbezeichner vorfand — die eigentlich zu prüfende Logik wurde nie erreicht.
Er leitete einen vermuteten Fehler ab: Ein Dokument ohne Typ dürfe die Auswertung nicht scheitern lassen. Die Klärung ergab, dass der Code korrekt, der Test aber missverständlich benannt war. Geprüft wurde eine Sicherheitseigenschaft: dass ein Benutzer, der den Kundennamen als Pfadbestandteil in die Adresse schreibt, kein Ergebnis erhält und keine Anfrage an den Speicher ausgelöst wird.
Der Vorgang zeigt zweierlei: Ein Test, der aus dem falschen Grund grün ist, ist wertlos, weil er Sicherheit suggeriert. Der Fund war nicht durch Ausführen zu erzielen, sondern nur durch Lesen — er belegt den Wert des Reviews für Testcode.
Als Konsequenz wurde die Absicht des Tests explizit gemacht. Diese Anforderung — dass ein Test ohne den Fix fehlschlagen muss — wurde in die Akzeptanzkriterien des Folgeitems zur Nebenläufigkeit aufgenommen (siehe Abschnitt~\ref{sec:race-conditions}).
Binary file not shown.
+10 -5
View File
@@ -1,8 +1,10 @@
gantt gantt
title Zeitplanung PidI 3 (Sprints 15–17.2026) title Zeitplanung PidI 3 (Sprints 15–17.2026)
dateFormat YYYY-MM-DD dateFormat YYYY-MM-DD
section Planung section Vorlauf
Feature-Analyse :done, 2026-06-18, 1d Feature-Analyse :done, 2026-06-18, 1d
section Sprint 14.2026
Praxiseinsatz Beginn :milestone, done, 2026-07-07, 0d
Anforderungen & Backlog :done, 2026-07-07, 5d Anforderungen & Backlog :done, 2026-07-07, 5d
section Sprint 15.2026 section Sprint 15.2026
Document Explorer :done, 2026-07-27, 5d Document Explorer :done, 2026-07-27, 5d
@@ -10,9 +12,12 @@ gantt
Downloads & PDF-Modal :done, 2026-08-15, 7d Downloads & PDF-Modal :done, 2026-08-15, 7d
section Sprint 16.2026 section Sprint 16.2026
Typfilter & Pagination :done, 2026-08-18, 4d Typfilter & Pagination :done, 2026-08-18, 4d
Ordner-Lookup :active, 2026-08-17, 8d Ordner-Lookup :done, 2026-08-17, 10d
section Sprint 17.2026 Praxiseinsatz Ende :milestone, done, 2026-08-25, 0d
Race Conditions (10070) :2026-08-26, 5d section Sprint 17.2026 (Nachlauf)
Bugfix Suchfeld (10134) :done, 2026-08-27, 1d
Handbuch (10167) :done, 2026-08-31, 3d
Race Conditions (10070) :2026-09-08, 5d
section Qualitätssicherung section Qualitätssicherung
Abnahmetest :done, 2026-08-13, 12d Abnahmetest :done, 2026-08-13, 12d
Release :2026-08-26, 3d Produktivsetzung :milestone, done, 2026-09-03, 0d
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+11 -4
View File
@@ -1,4 +1,11 @@
% \chapter*{Kurzfassung} \chapter*{Kurzfassung}
% \addcontentsline{toc}{chapter}{Kurzfassung} \addcontentsline{toc}{chapter}{Kurzfassung}
% TODO
% \clearpage Das Praktikum dokumentiert die Entwicklung des Houston-Moduls „Dokumente" — ein kundenseitiger Dokumentenbereich im Portal der WorkSimple GmbH. Das System stellt Kundendokumente aus einem S3-Speicher (NetApp StorageGRID) bereit und kombiniert mehrere Anforderungen: Document Explorer, Suche, Downloadfunktionalität, PDF-Vorschau, Typfilter, Pagination und automatische Ordnerstrukturanlage.
Die Kernherausforderung lag in einer Mandatsparitäts-Diskrepanz: Houston autorisiert über die Efecte-Organisations-ID, das Kundenservice-Management pflegt S3-Ordner aber über den Kundennamen. Der Speicher bietet keine Metadatenschlüsselung für Lookups. Die Lösung verzichtet auf Metadatensuche und typisiert Dokumente stattdessen über Ordnerstruktur (Präfix-Listing), erfordert aber gleichzeitig automatische Ordneranlage bei Erstaccess. Eine Nebenläufigkeitsanalyse deckte zwei kritische Szenarien bei gleichzeitigen Operationen auf, die als separates Backlog Item 10070 deferred wurden.
Die Umsetzung umfasst 13 zusammengeführte Pull Requests, 114 Code-Review-Kommentare, Acceptance-Testing mit Fehlerbearbeitung und Produktivsetzung am 03.~September 2026. Alle 13 funktionalen Anforderungen wurden ausgeliefert; fünf von sechs nichtfunktionalen Anforderungen sind erfüllt. Der Aufwand verteilte sich über die Sprints 15–17.2026, mit 24 Manntagen im offiziellen Tätigkeitszeitraum 07.07.--25.08.2026.
Die dokumentierten Erkenntnisse umfassen Lookup-Architekturmuster, Race-Condition-Risikoanalyse, ZIP-Streaming-Implementierung, PDF-Vorschau-Fallstricke und Facetten der S3-Clientkonfiguration unter Fehlertoleranzanforderungen.
\clearpage
+2 -2
View File
@@ -20,8 +20,8 @@
{Sarah Hinzmann} {Sarah Hinzmann}
\setdates \setdates
{07.07.2026 -- 25.08.2026} {07.07.2026 -- 03.09.2026}
{25.08.2026} {17.09.2026}
\begin{document} \begin{document}