464 lines
18 KiB
Plaintext
464 lines
18 KiB
Plaintext
@startuml
|
|
' skinparam linetype ortho
|
|
' skinparam groupInheritance 2
|
|
allowmixing
|
|
|
|
package subscriptionsAPI <<Frame>> {
|
|
package subscriptionDataAccessLayer <<Frame>> {
|
|
class SubscriptionDataAccessService <<@Repository>> {
|
|
<<create>> SubscriptionDataAccessService(JpaTemplate jpaTemplate)
|
|
int uploadSubscriptions(String username, List<SubscriptionAction> subscriptions)
|
|
List<String> getSubscriptions(String username)
|
|
List<String> getSubscriptionsSince(String username, LocalDateTime time)
|
|
int addSubscriptions(String username, List<SubscriptionAction> addedSubscriptions)
|
|
int removeSubscriptions(String username, List<SubscriptionAction> removedSubscriptions)
|
|
List<SubscriptionTitles> getTitles(String username)
|
|
}
|
|
|
|
interface SubscriptionDao {
|
|
int uploadSubscriptions(String username, List<SubscriptionAction> subscriptions)
|
|
List<String> getSubscriptions(String username)
|
|
List<String> getSubscriptionsSince(String username, LocalDateTime time)
|
|
int addSubscriptions(String username, List<SubscriptionAction> addedSubscriptions)
|
|
int removeSubscriptions(String username, List<SubscriptionAction> removedSubscriptions)
|
|
List<SubscriptionTitles> getTitles(String username)
|
|
}
|
|
}
|
|
|
|
package subscriptionService <<Frame>> {
|
|
class SubscriptionService <<@Service>> {
|
|
<<create>> SubscriptionService(SubscriptionDao subscriptionDao)
|
|
int uploadSubscriptions(String username, List<SubscriptionAction> subscriptions)
|
|
List<String> getSubscriptions(String username)
|
|
List<String> getSubscriptionsSince(String username, LocalDateTime time)
|
|
int addSubscriptions(String username, List<SubscriptionAction> addedSubscriptions)
|
|
int removeSubscriptions(String username, List<SubscriptionAction> removedSubscriptions)
|
|
List<SubscriptionTitles> getTitles(String username)
|
|
}
|
|
}
|
|
|
|
package subscriptionController <<Frame>> {
|
|
class SubscriptionController <<@Controller>>{
|
|
' @Autowired
|
|
<<create>> SubscriptionController(SubscriptionService subscriptionService)
|
|
' @GetMapping
|
|
ResponseEntity<List<String>> getSubscriptions(String username, String deviceID, String functionJSONP)
|
|
' @PutMapping
|
|
ResponseEntity<String> uploadSubscriptions(String username, String deviceID, List<String> subscriptions)
|
|
' @PostMapping
|
|
ResponseEntity<SubscriptionDelta> applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta)
|
|
' @GetMapping
|
|
ResponseEntity<SubscriptionDelta> getSubscriptionDelta(String username, String deviceID, long since)
|
|
ResponseEntity<List<SubscriptionTitles>> getTitles(String username, String deviceID)
|
|
}
|
|
|
|
class SubscriptionTitles {
|
|
<<create>> SubscriptionTitles(Subscription subscription, List<EpisodeActionPost> episodeTitles)
|
|
Subscription getSubscription()
|
|
List<EpisodeActionPost> getEpisodesTitles()
|
|
}
|
|
|
|
class SubscriptionDelta {
|
|
<<create>> SubscriptionDelta(List<String> add, List<String> remove)
|
|
List<String> getRemove()
|
|
LocalDate getTimestamp()
|
|
List<List<String>> getUpdate_urls()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
package episodeActionsAPI <<Frame>> {
|
|
package episodeActionDataAccessLayer <<Frame>> {
|
|
class EpisodeActionDataAccessService <<@Repository>> {
|
|
<<create>> EpisodeActionDataAccessService (JpaTemplate jpaTemplate)
|
|
long addEpisodeActions(String username, List<EpisodeActionPost> episodeActionPosts)
|
|
List<EpisodeActionPost> getEpisodeActions(String username)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcast(String username, String podcastURL)
|
|
List<EpisodeActionPost> getEpisodeActionsSince(String username, LocalDateTime since)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since)
|
|
}
|
|
|
|
interface EpisodeActionDao {
|
|
long addEpisodeActions(String username, List<EpisodeActionPost> episodeActionPosts)
|
|
List<EpisodeActionPost> getEpisodeActions(String username)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcast(String username, String podcastURL)
|
|
List<EpisodeActionPost> getEpisodeActionsSince(String username, LocalDateTime since)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since)
|
|
}
|
|
}
|
|
|
|
package episodeActionService <<Frame>> {
|
|
class EpisodeActionService <<@Service>> {
|
|
<<create>> EpisodeActionService (EpisodeActionDao episodeActionDao)
|
|
LocalDateTime addEpisodeActions(String username, List<EpisodeActionPosts> episodeActionPosts)
|
|
List<EpisodeActionPost> getEpisodeActions(String username)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcast(String username, String podcastURL)
|
|
List<EpisodeActionPost> getEpisodeActionsSince(String username, LocalDateTime since)
|
|
List<EpisodeActionPost> getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since)
|
|
}
|
|
}
|
|
|
|
package episodeActionController <<Frame>> {
|
|
class EpisodeActionController <<@Controller>>{
|
|
<<create>> EpisodeActionController (EpisodeActionService episodeActionService)
|
|
ResponseEntity<EpisodeActionPostResponse> addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest)
|
|
ResponseEntity<EpisodeActionGetResponse> getEpisodeActions(String username, String deviceID, boolean aggregated)
|
|
ResponseEntity<EpisodeActionGetResponse> getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated)
|
|
ResponseEntity<EpisodeActionGetResponse> getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated)
|
|
ResponseEntity<EpisodeActionGetResponse> getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated)
|
|
}
|
|
|
|
class EpisodeActionPostResponse {
|
|
<<create>> EpisodeActionPostResponse(List<Pair<String, String>> updateURLs)
|
|
long getTimestamp()
|
|
List<Pair<String, String>> getUpdatedURLs()
|
|
}
|
|
|
|
class EpisodeActionPost {
|
|
<<create>> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position)
|
|
String getPodcastURL()
|
|
String getEpisodeURL()
|
|
int getGUID()
|
|
Action getAction()
|
|
LocalDateTime getTimestamp()
|
|
int getStarted()
|
|
int getPosition()
|
|
EpisodeAction getEpisodeAction()
|
|
}
|
|
|
|
class EpisodeActionPostRequest {
|
|
<<create>> EpisodeActionPostRequest(List<EpisodeActionPost> episodeActionPosts)
|
|
List<EpisodeActionPost> getEpisodeActionPosts()
|
|
}
|
|
|
|
class EpisodeActionGetResponse {
|
|
<<create>> EpisodeActionGetResponse(List<EpisodeActionPost> episodeActionPosts)
|
|
List<EpisodeActionPost> getEpisodeActionPosts()
|
|
long getTimestamp()
|
|
}
|
|
}
|
|
}
|
|
|
|
package authenticationAPI <<Frame>> {
|
|
package authenticationDataAccessLayer <<Frame>> {
|
|
' interface AuthenticationDao {
|
|
' String login(String username)
|
|
' int logout(String username)
|
|
' }
|
|
|
|
' class AuthenticationDataAccessService <<@Respository>> {
|
|
' <<create>> AuthenticationDataAccessService(JpaTemplate jpaTemplate)
|
|
' String login(String username)
|
|
' int logout(String username)
|
|
' }
|
|
|
|
interface UserDetailsManager {
|
|
void createUser(UserDetails userDetails)
|
|
void changePassword(String oldPassword, String newPassword)
|
|
void deleteUser(String username)
|
|
void updateUser(UserDetails user)
|
|
boolean userExists(String username)
|
|
}
|
|
note left
|
|
Aus org.springframework.security.provisioning
|
|
- liefert Methoden zum Erstellen neuer User
|
|
und zum Aktualisieren bestehender.
|
|
end note
|
|
|
|
class JdbcUserDetailsManager <<@Repository>> {
|
|
<<create>> JdbcUserDetailsManager(DataSource dataSource)
|
|
void createUser(UserDetails user)
|
|
void changePassword(String oldPassword, String newPassword)
|
|
void deleteUser(String username)
|
|
void updateUser(UserDetails user)
|
|
boolean userExists(String username)
|
|
}
|
|
note right
|
|
User Management Service aus dem Paket
|
|
org.springframework.security.provisioning
|
|
der CRUD Operationen für User bereitstellt.
|
|
Hier sind nur die relevanten Methoden modelliert.
|
|
end note
|
|
}
|
|
|
|
package authenticationService <<Frame>> {
|
|
class AuthenticationService <<@Service>> {
|
|
--
|
|
<<create>> AuthenticationService(UserDetailsManager userDetailsManager)
|
|
List<String> verifyLogin(String username)
|
|
int logout(String username)
|
|
int forgotPassword(ForgotPasswordRequest forgotPasswordRequest)
|
|
.. via JdbcUserDetailsManager ..
|
|
int resetPassword(String username, RequestWithPassword requestWithPassword)
|
|
int registerUser(UserDetails user)
|
|
int changePassword(String username, ChangePasswordRequest changePasswordRequest)
|
|
int deleteUser(String username, RequestWithPassword requestWithPassword)
|
|
}
|
|
|
|
class JavaMailSenderImpl {}
|
|
note left
|
|
Aus org.springframework.mail.javamail.
|
|
Implementierung des JavaMailSender Interfaces,
|
|
welches das MailSender Interface durch Unterstützung
|
|
von MIME Nachrichten erweitert.
|
|
Das MailSender Interface definiert dabei eine
|
|
Strategie zum Versenden einfacher Mails.
|
|
Unterstützt sowohl JavaMail MimeMessages und
|
|
Spring SimpleMailMessages.
|
|
end note
|
|
}
|
|
|
|
package authenticationController <<Frame>> {
|
|
class AuthenticationController <<@Controller>> {
|
|
<<create>> AuthenticationController(AuthenticationService authenticationService)
|
|
ResponseEntity<List<String>> verifyLogin(String username)
|
|
ResponseEntity<Integer> logout(String username)
|
|
ResponseEntity<Integer> forgotPassword(ForgotPasswordRequest forgotPasswordRequest)
|
|
ResponseEntity<Integer> resetPassword(String username, RequestWithPassword requestWithPassword)
|
|
ResponseEntity<Integer> registerUser(UserDetails user)
|
|
ResponseEntity<Integer> changePassword(String username, ChangePasswordRequest changePasswordRequest)
|
|
ResponseEntity<Integer> deleteUser(String username, RequestWithPassword requestWithPassword)
|
|
}
|
|
|
|
class ChangePasswordRequest {
|
|
<<create>> ChangePasswordRequest(String oldPassword, String newPassword)
|
|
String getOldPassword()
|
|
String getNewPassword()
|
|
}
|
|
|
|
class ForgotPasswordRequest {
|
|
<<create>> ForgotPasswordRequest(String email)
|
|
String getEmail()
|
|
}
|
|
|
|
class RequestWithPassword {
|
|
<<create>> ResetPasswordRequest(String password)
|
|
String getPassword()
|
|
}
|
|
}
|
|
}
|
|
|
|
package model <<Frame>> {
|
|
class Subscription {
|
|
<<create>> Subscription(String url, String title)
|
|
int getID()
|
|
String getURL()
|
|
long getLastActionTimestamp()
|
|
String getTitle()
|
|
}
|
|
|
|
class SubscriptionAction {
|
|
<<create>> SubscriptionAction(int userID, int subscriptionID)
|
|
int getID()
|
|
int getUserID()
|
|
int getSubscriptionID()
|
|
long getTimestamp()
|
|
boolean getAdded()
|
|
}
|
|
|
|
class Episode {
|
|
<<create>> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total)
|
|
int getSubscriptionID()
|
|
int getID()
|
|
int getGUID()
|
|
String getURL()
|
|
String getTitle()
|
|
int getTotal()
|
|
}
|
|
|
|
enum Action {
|
|
Download
|
|
Play
|
|
Delete
|
|
New
|
|
Flattr
|
|
String getJsonProperty()
|
|
}
|
|
|
|
class EpisodeAction {
|
|
<<create>> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position)
|
|
int getEpisodeID()
|
|
Action getAction()
|
|
long getTimestamp()
|
|
int getStarted()
|
|
int getPosition()
|
|
void setEpisodeID()
|
|
EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL)
|
|
}
|
|
|
|
interface UserDetails {
|
|
String getUsername()
|
|
String getPassword()
|
|
Collection<Authority> getAuthorities()
|
|
boolean isAccountExpired()
|
|
boolean isAccountLocked()
|
|
boolean isCredentialsNonExpired()
|
|
boolean isEnabled()
|
|
}
|
|
note left
|
|
Aus org.springframework.security.core.userdetails.
|
|
Wird für die Schnittstelle UserDetailsManager benötigt.
|
|
Stellt wichtige Informationen eines Users bereit.
|
|
Diese werden nur indirekt von Spring Security
|
|
benutzt, indem sie vorher in Authentication Objekten
|
|
gekapselt werden.
|
|
end note
|
|
|
|
class User {
|
|
--
|
|
<<create>> User(String username, String password)
|
|
int getID()
|
|
String getSessionToken()
|
|
boolean getEmailIsValidated()
|
|
.. interface methods ..
|
|
String getUsername()
|
|
String getPassword()
|
|
Collection<Authority> getAuthorities()
|
|
boolean isAccountExpired()
|
|
boolean isAccountLocked()
|
|
boolean isCredentialsNonExpired()
|
|
boolean isEnabled()
|
|
}
|
|
|
|
interface GrantedAuthority {
|
|
String getAuthority()
|
|
}
|
|
note right
|
|
Aus org.springframework.security.core.
|
|
Wird für die Schnittstelle UserDetails benötigt.
|
|
Repräsentiert eine Autorisierung, die einem
|
|
Authentication Objekt gewährt wird.
|
|
end note
|
|
|
|
class Authority {
|
|
<<create>> Authority()
|
|
String getAuthority()
|
|
}
|
|
}
|
|
|
|
package util <<Frame>> {
|
|
class RSSParser {
|
|
<<create>> RSSParser(String subscriptionURL)
|
|
String getSubscriptionTitle()
|
|
List<Episode> getEpisodes()
|
|
Episode getEpisodeForURL(String episodeURL)
|
|
}
|
|
note bottom
|
|
Verwendet intern Spring um
|
|
HTTP-Anfragen zu erstellen.
|
|
end note
|
|
|
|
class CleanCronJob {
|
|
<<create>> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager)
|
|
void cleanInvalidUsers()
|
|
}
|
|
note bottom
|
|
Hintergrundservice, der in periodischen Abständen
|
|
Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden
|
|
bestätigt haben, wieder aus der Datenbank löscht.
|
|
(Auf die Assoziation zu JdbcUserDetailsManager wird
|
|
im Sinne der Übersichtlichkeit verzichtet.)
|
|
end note
|
|
|
|
class ResponseEntity<T> {
|
|
<<create>> ResponseEntity(T body, HttpStatusCode status)
|
|
T getBody()
|
|
HttpStatusCode getStatusCode()
|
|
}
|
|
note bottom
|
|
Aus org.springframework.http.
|
|
Erweitert die Klasse HttpEntity, welche
|
|
ein HTTP Anfrage- oder Antwort-Objekt
|
|
repräsentiert, durch einen HttpStatusCode.
|
|
Wird von den Controller-Methoden als
|
|
Rückgabewert verwendet.
|
|
end note
|
|
}
|
|
|
|
class SecurityConfigurationBasicAuth {
|
|
<<create>> SecurityConfigurationBasicAuth()
|
|
PasswordEncoder encoder()
|
|
UserDetailsManager userDetailsService()
|
|
SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion
|
|
}
|
|
note top
|
|
Erstellt einen Servlet Filter (springSecurityFilterChain)
|
|
der für die gesamte Sicherheit zuständig ist (Schutz der URLs,
|
|
Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.).
|
|
end note
|
|
|
|
class PSEApplication {
|
|
<<create>> PSEApplication()
|
|
void main(String[] args)
|
|
}
|
|
|
|
database Datenbank
|
|
Datenbank <-[hidden]d- subscriptionsAPI
|
|
Datenbank <-[hidden]d- episodeActionsAPI
|
|
Datenbank <-[hidden]d- authenticationAPI
|
|
() SQL as SQLSub
|
|
() SQL as SQLAuth
|
|
() SQL as SQLEpisode
|
|
|
|
Datenbank -- SQLSub
|
|
Datenbank -- SQLAuth
|
|
Datenbank -- SQLEpisode
|
|
|
|
SubscriptionController ..o PSEApplication
|
|
AuthenticationController ..o PSEApplication
|
|
EpisodeActionController ..o PSEApplication
|
|
SecurityConfigurationBasicAuth ..o PSEApplication
|
|
|
|
PSEApplication --() HTTP
|
|
|
|
SQLSub )-- SubscriptionDataAccessService: JPA
|
|
' SQLAuth )-- AuthenticationDataAccessService: JPA
|
|
SQLAuth )-- JdbcUserDetailsManager: JDBC
|
|
SQLEpisode )-- EpisodeActionDataAccessService: JPA
|
|
|
|
Subscription <. SubscriptionAction: ID
|
|
' Subscription <.. SubscriptionDataAccessService: DB
|
|
' SubscriptionAction <.. SubscriptionDataAccessService: DB
|
|
SubscriptionService --o SubscriptionController
|
|
SubscriptionDao <.. SubscriptionService: <<use>>
|
|
Subscription --o SubscriptionTitles
|
|
EpisodeActionPost -o SubscriptionTitles
|
|
SubscriptionDao <|. SubscriptionDataAccessService: <<realize>>
|
|
|
|
' User <.. AuthenticationDataAccessService: DB
|
|
' User <.. JdbcUserDetailsManager: DB
|
|
UserDetailsManager <.. AuthenticationService: <<use>>
|
|
' AuthenticationDao <.. AuthenticationService: <<use>>
|
|
AuthenticationService --o AuthenticationController
|
|
' AuthenticationDao <|. AuthenticationDataAccessService: <<realize>>
|
|
UserDetailsManager <|. JdbcUserDetailsManager: <<realize>>
|
|
UserDetailsManager <.. SecurityConfigurationBasicAuth: <<use>>
|
|
UserDetails <|.. User: <<realize>>
|
|
User -> Authority
|
|
GrantedAuthority <|.. Authority: <<realize>>
|
|
JavaMailSenderImpl <. AuthenticationService: <<use>>
|
|
|
|
Action <-- EpisodeAction
|
|
EpisodeActionPost -o EpisodeActionGetResponse
|
|
EpisodeActionPost -o EpisodeActionPostRequest
|
|
EpisodeAction .> Episode: ID
|
|
' EpisodeAction <.. EpisodeActionDataAccessService: DB
|
|
' Episode <.. EpisodeActionDataAccessService: DB
|
|
EpisodeActionDao <.. EpisodeActionService: <<use>>
|
|
EpisodeActionService --o EpisodeActionController
|
|
EpisodeActionDao <|. EpisodeActionDataAccessService: <<realize>>
|
|
|
|
RSSParser <. SubscriptionDataAccessService: <<use>>
|
|
RSSParser <. EpisodeActionDataAccessService: <<use>>
|
|
' JdbcUserDetailsManager <-- CleanCronJob
|
|
|
|
model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode)
|
|
' Datenbank o.. Subscription: ORM
|
|
' Datenbank o.. SubscriptionAction: ORM
|
|
' Datenbank o.. Episode: ORM
|
|
' Datenbank o.. EpisodeAction: ORM
|
|
' Datenbank o.. User: ORM
|
|
|
|
@enduml
|