\newenvironment{urlParameter} { \newcommand{\urlParamItem}[2] { \rowcolor{\methodLightColor} ##1 & ##2 \\ } \newcommand{\noUrlParameter}[1] { \small{\textit{##1}} } %\vspace{-0.61em} \arrayrulecolor{\methodColor} \begin{tabularx}{\textwidth}{X} \rowcolor{\methodLightColor!20} \textbf{URL-Parameter} \\ \hline \end{tabularx} \tabularx{\textwidth}{l X} } \newenvironment{pathParameter} { \newcommand{\pathParamItem}[2] { \rowcolor{\methodLightColor} ##1 & ##2 \\ } \newcommand{\noPathParameter}[1] { \small{\textit{##1}} } %\vspace{-0.61em} \arrayrulecolor{\methodColor} \begin{tabularx}{\textwidth}{X} \rowcolor{\methodLightColor!20} \textbf{Pfad-Parameter} \\ \hline \end{tabularx} \tabularx{\textwidth}{l X} } \newenvironment{jsonKeys} { \newcommand{\jsonKeyItem}[2] { \rowcolor{\methodLightColor} ##1 & ##2 \\ } \newcommand{\nojsonKeys}[1] { \small{\textit{##1}} } %\vspace{-0.61em} \arrayrulecolor{\methodColor} \begin{tabularx}{\textwidth}{X} \rowcolor{\methodLightColor!20} \textbf{Json-Keys} \\ \hline \end{tabularx} \tabularx{\textwidth}{l X} } \section{Änderungen zum Entwurfsheft} Im Folgenden werden die Änderungen zum Entwurfsheft aufgelistet und erläutert warum diese Änderungen gemacht wurden. \subsection{Kompatibilität mit \Glspl{podcatcher}} Um die Kompatibilität mit \Glspl{podcatcher} - insbesondere AntennaPod und Kasts - sicherzustellen, mussten einige Änderungen vorgenommen werden. \subsubsection*{Speicherung von Benutzernamen und E-Mail-Adressen} AntennaPod erlaubt bei der Verknüpfung mit einem Synchronisations-Server nur die Anmeldung mit einem Benutzername, der keine E-Mail-Adresse ist. Deshalb werden nun sowohl ein Benutzername als auch eine E-Mail-Adresse für jeden Nutzer gespeichert. Der Benutzername wird dabei unverschlüsselt gespeichert. Er wird zur Authentifizierung sowie für die Zuordnung der Anfragen verwendet. Die E-Mail-Adresse hingegen, wird vor der Speicherung mit einem festen Geheimschlüssel gesalted und anschließend gehashed. Sie existiert lediglich um bei einer Passwort-vergessen-Anfrage denjenigen Nutzer zu finden, zu dem die übergebene E-Mail-Adresse gehört, beziehungsweise dessen Existenz zu überprüfen. Diese Änderung impliziert eine Anpassung des Formats, in dem Registrierungsanfragen als \GLS{json}-Payload übergeben werden. Wie in der folgenden aktualisierten Spezifikation zu sehen existiert nun ein zusätzliches \GLS{json}-Attribut namens \enquote{username}. \newline \begin{apiRoute}{post}{/api/2/auth/register.json} {Registriert einen Nutzer mit einer E-Mail-Adresse und Passwort. Versendet E-Mail mit Bestätigungslink an die angegebene E-Mail-Adresse.} \begin{routeRequest}{application/json} \begin{routeRequestBody} { "username": "jeff", "email": "jeff@example.com", "password": "MyNameIsJeff" } \end{routeRequestBody} \end{routeRequest} \begin{routeResponse}{application/json} \begin{routeResponseItem}{200} {OK: Nutzer wurde erfolgreich angelegt und E-Mail versendet.} \end{routeResponseItem} \begin{routeResponseItem}{400} {Bad Request: Fehler beim Parsen oder Eingabe nicht anforderungsgemäß.} \end{routeResponseItem} \end{routeResponse} \end{apiRoute} \subsubsection*{Device API} Beim Einrichten eines Synchronisations-Servers rufen sowohl AntennaPod als auch Kasts statt einem anfänglichen Login sofort den List-Devices-Endpunkt der \gls{gpodder}.net API auf. Dieser Endpunkt wurde entsprechend hinzugefügt. Da jedoch explizit keine Unterscheidung von Geräten bei der Synchronisation unterstützt wird, wird dieser intern wie ein Login-Aufruf behandelt. \newline \begin{apiRoute}{get}{/api/2/devices/\{username\}.json} {Gegebenen Nutzer des gegebenen Geräts mithilfe HTTP Basic Auth einloggen oder Gültigkeit des im \enquote{sessionid} \Gls{cookie} gespeicherten JWTs bestätigen. Gibt außerdem eine Liste mit einem Dummy-Device zurück, damit die Einrichtung der Synchronisation mit AntennaPod und Kasts möglich ist.} \begin{pathParameter} \pathParamItem{username}{Nutzername des einzuloggenden Nutzers} \end{pathParameter} \begin{routeResponse}{application/json} \begin{routeResponseItem}{200} {OK: Der Benutzer wurde erfolgreich mittles HTTP Basic Auth oder JWT eingeloggt und das \enquote{sessionid} Cookie wurde auf ein gültiges JWT gesetzt.} \newline \begin{routeResponseItemBody} [ { "id": "dummy", "caption": "device", "type": "other", "subscriptions": 0 } ] \end{routeResponseItemBody} \end{routeResponseItem} \begin{routeResponseItem}{401} {Unauthorized: Es liegen falsche Anmeldedaten oder ein ungültiges JWT vor.} \end{routeResponseItem} \end{routeResponse} \begin{jsonKeys} \jsonKeyItem{id}{Geräte-ID} \jsonKeyItem{caption}{Ein für Menschen lesbarer Name für das Gerät} \jsonKeyItem{type}{Typ des Geräts - mögliche Typen: desktop, laptop, mobile, server, other} \jsonKeyItem{subscriptions}{Anzahl der Subscriptions auf dem Gerät} \end{jsonKeys} \end{apiRoute} \newpage \subsection{Verifizierung der E-Mail-Adresse} Dieser Endpunkt wurde zur Verifizierung der bei der Registrierung angegebenen E-Mail-Adresse hinzugefügt. Nach der Registrierung wird dem Benutzer eine E-Mail mit der URL dieses Endpunkts (inklusive Benutzernamen und JWT) zugesendet. Klickt der Benutzer auf den Link wird die Anfrage im Backend verarbeitet und der Nutzer automatisch zum Webfrontend weitergeleitet. Erst nach der Verifizierung der E-Mail-Adresse ist die Registrierung vollständig abgeschlossen und der Account aktiviert - nun kann sich der Nutzer anmelden. \newline \begin{apiRoute}{get}{/api/2/auth/\{username\}/verify.json} {Verifiziere die bei der Registrierung angegebene E-Mail-Adresse durch diese, per E-Mail versendete, URL.} \begin{pathParameter} \pathParamItem{username}{Nutzername des betreffenden Nutzers} \end{pathParameter} \begin{urlParameter} \urlParamItem{token}{JSON-Web-Token (24h gültig)} \end{urlParameter} \begin{routeResponse}{application/json} \begin{routeResponseItem}{200} {OK: Der Benutzer wurde erfolgreich aktiviert und kann sich nun anmelden.} \end{routeResponseItem} \begin{routeResponseItem}{400} {Bad Request: Der Nutzer mit dem angegebenen Namen ist bereits verifiziert. } \end{routeResponseItem} \begin{routeResponseItem}{401} {Unauthorized: Der JWT ist ungültig. } \end{routeResponseItem} \begin{routeResponseItem}{404} {Not Found: Es exisitiert kein Nutzer mit dem angegebenen Benutzernamen. } \end{routeResponseItem} \end{routeResponse} \end{apiRoute} \newpage \subsection{RSSParser} Das primäre Ziel beim Entwurf des RSSParsers war es den Rest der Anwendung nicht aufzuhalten, da das fetchen und parsen länger dauert. Daher soll der RSSParser asynchron ausgeführt werden. Damit das für die Implementierung verwendete Framework \Gls{spring} dies unterstützt müssen mehr Bedingungen erfüllt sein als im Entwurf berücksichtigt wurden, weshalb dieser abgeändert werden musste. Unter anderem können nur Komponenten die von \Gls{spring} verwaltet werden asynchron ausgeführt werden. Weiter können nur öffentliche Methoden von \Gls{spring} als asynchron erkannt werden und bei dem Aufruf einer asynchronen Methode muss die Klasse gewechselt werden damit diese asynchron ausgeführt wird. Um diese Bedingungen zu erfüllen ruft der SubscriptionService oder der EpisodeActionService die Validate Methode des RSSParsers mit der Subscription die überprüft werden soll auf. Dies geschieht asynchron nach dem \enquote{Fire and Forget} Prinzip. Daher können die Services unmittelbar weiter arbeiten und dem Nutzer so möglichst schnell eine Antwort liefern. Die Validate Methode fragt den aktuellen \GLS{rss}-Feed der Subscription ab und parsed diesen. Erfüllt der Feed die von Apple und Google für \Glspl{podcast} definierten Anforderungen werden die Informationen aus dem Feed gespeichert. Dabei werden nur die \Glspl{episode} gespeichert, die von mindestens einem Nutzer gehört wurden. Ist der Feed der Subscription nicht valide wird die Subscription gelöscht. Um das Löschen und Speichern ausführen zu können hält der Parser Referenzen auf das SubscriptionDao und EpisodeDao. Die im Entwurfsheft definierten Getter werden nicht mehr oder nur noch als private Hilfsmethoden benötigt. \subsection{Reduzierte Datenzugriffsschicht} Mithilfe von JPA Repositories können Datenbankzugriffe und Abfragen ohne weitere Logik in den DAO-Schnittstellen einfach implementiert werden. Wird beispielsweise nach einem \Gls{abo} anhand einer URL gesucht, so wird eine Methode der Form \textit{Optional findByUrl(String url)} in der SubscriptionDao deklariert. Um die Implementierung dieser Abfrage kümmert sich JPA. Damit fallen alle ursprünglich geplanten DAO-Implementierungen weg, was für eine bessere Übersicht in der Datenzugriffsschicht sorgt. \newpage \begin{landscape} \subsection{Überarbeitetes Klassendiagramm des Backends} Das Klassendiagramm zeigt alle den überarbeiteten Entwurf des Backends. Weiter zeigt das Diagramm die Aufteilung der Klassen in Pakete sowie schemenhaft dargestellte Verbindungen zu \Gls{db} und Webserver. % \input{assets/diagrams/classdiagram.latex} \includegraphics[width=\linewidth]{assets/diagrams/class_after} \end{landscape}