chore(frontend): cleanup legacy code and improve localization consistency

- Removed deprecated `NotificationCard` component due to dependency on an outdated presentation layer.
- Translated comments and documentation to German for improved localization across `core.auth`, `ping-feature`, and `network`.
- Standardized comment formatting, improved doc clarity, and ensured consistent API documentation in all modules.
This commit is contained in:
2026-02-01 12:00:10 +01:00
parent 24c8a0c63d
commit af0b7c5f9b
16 changed files with 72 additions and 76 deletions
@@ -44,13 +44,13 @@ class AuthApiClient(
append("grant_type", "password")
append("client_id", clientId)
// IMPORTANT: Only send client_secret if it's NOT a public client (like 'web-app')
// Keycloak rejects requests from public clients that contain a client_secret.
// We check if the client ID suggests a public client or if secret is explicitly provided.
// For now, we rely on the fact that 'web-app' is public and should NOT have a secret sent.
// WICHTIG: Senden Sie client_secret nur, wenn es sich NICHT um einen öffentlichen Client (wie 'web-app') handelt.
// Keycloak lehnt Anfragen von öffentlichen Clients ab, die client_secret enthalten.
// Wir prüfen, ob die Client-ID auf einen öffentlichen Client hindeutet oder ob ein Secret explizit angegeben wurde.
// Aktuell gehen wir davon aus, dass 'web-app' öffentlich ist und daher kein Secret gesendet werden sollte.
// Logic: If clientId is 'web-app', we force ignore the secret, or we rely on caller to pass null.
// Since AppConstants might still have the secret for 'postman-client', we need to be careful.
// Logik: Wenn clientId 'web-app' ist, ignorieren wir das Geheimnis oder verlassen uns darauf, dass der Aufrufer null übergibt.
// Da AppConstants möglicherweise noch das Geheimnis für 'postman-client' enthält, ist Vorsicht geboten.
if (!clientSecret.isNullOrBlank() && clientId != "web-app") {
append("client_secret", clientSecret)
@@ -10,7 +10,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.time.ExperimentalTime
/**
* Client-side permission enumeration that mirrors server-side BerechtigungE
* Client-side Berechtigungsaufzählung, welche die serverseitige BerechtigungE widerspiegelt
*/
@Serializable
enum class Permission {
@@ -66,9 +66,9 @@ data class AuthState(
/**
* Secure in-memory JWT token manager
*
* For web clients, storing tokens in memory is the most secure approach
* to prevent XSS attacks. The token is lost when the browser tab is closed
* or refreshed, requiring re-authentication.
* Für Webclients ist das Speichern von Tokens im Arbeitsspeicher der sicherste Ansatz,
* um XSS-Angriffe zu verhindern. Das Token geht beim Schließen des Browser-Tabs verloren,
* was eine erneute Authentifizierung erforderlich macht.
*/
@Suppress("unused")
class AuthTokenManager {
@@ -141,38 +141,38 @@ class AuthTokenManager {
fun getUserId(): String? = tokenPayload?.sub
/**
* Get username from token
* Get username from a token
*/
fun getUsername(): String? = tokenPayload?.username
/**
* Get current user permissions
* Get current user permissions (Berechtigungen)
*/
fun getPermissions(): List<Permission> = _authState.value.permissions
/**
* Check if user has a specific permission
* Check if the user has a specific permission
*/
fun hasPermission(permission: Permission): Boolean {
return _authState.value.permissions.contains(permission)
}
/**
* Check if user has any of the specified permissions
* Check if the user has any of the specified permissions
*/
fun hasAnyPermission(vararg permissions: Permission): Boolean {
return permissions.any { _authState.value.permissions.contains(it) }
}
/**
* Check if user has all of the specified permissions
* Check if the user has all the specified permissions
*/
fun hasAllPermissions(vararg permissions: Permission): Boolean {
return permissions.all { _authState.value.permissions.contains(it) }
}
/**
* Check if user can perform read operations
* Check if the user can perform read operations
*/
fun canRead(): Boolean {
return hasAnyPermission(
@@ -184,7 +184,7 @@ class AuthTokenManager {
}
/**
* Check if user can perform create operations
* Check if the user can perform create operations
*/
fun canCreate(): Boolean {
return hasAnyPermission(
@@ -196,7 +196,7 @@ class AuthTokenManager {
}
/**
* Check if user can perform update operations
* Check if the user can perform update operations
*/
fun canUpdate(): Boolean {
return hasAnyPermission(
@@ -208,7 +208,7 @@ class AuthTokenManager {
}
/**
* Check if user can perform delete operations (admin-level)
* Check if the user can perform delete operations (admin-level)
*/
fun canDelete(): Boolean {
return hasAnyPermission(
@@ -220,12 +220,12 @@ class AuthTokenManager {
}
/**
* Check if user is admin (has delete permissions)
* Check if the user is admin (has deleted permissions)
*/
fun isAdmin(): Boolean = canDelete()
/**
* Check if token expires within specified minutes
* Check if the token expires within specified minutes
*/
@OptIn(ExperimentalTime::class)
fun isTokenExpiringSoon(minutesThreshold: Int = 5): Boolean {
@@ -238,8 +238,8 @@ class AuthTokenManager {
}
/**
* Parse JWT payload for basic validation and user info extraction
* Note: This is for client-side info extraction only, not security validation
* JWT-Payload für grundlegende Validierung und Extraktion von Benutzerinformationen analysieren
* Hinweis: Dies dient ausschließlich der clientseitigen Informationsextraktion, nicht der Sicherheitsvalidierung.
*/
@OptIn(ExperimentalEncodingApi::class)
private fun parseJwtPayload(token: String): JwtPayload? {
@@ -250,7 +250,7 @@ class AuthTokenManager {
// Decode the payload (second part)
val payloadJson = Base64.decode(parts[1]).decodeToString()
// First try to parse with standard approach
// First, try to parse with a standard approach
val basicPayload = try {
Json.decodeFromString<JwtPayload>(payloadJson)
} catch (e: Exception) {
@@ -263,7 +263,7 @@ class AuthTokenManager {
return basicPayload
}
// Otherwise, extract permissions manually from JSON string
// Otherwise, extract permissions manually from a JSON string
val permissions = extractPermissionsFromJson(payloadJson)
// Return payload with manually extracted permissions
@@ -282,16 +282,16 @@ class AuthTokenManager {
}
/**
* Extract permissions array from JSON string using simple string parsing
* Extract permissions array from a JSON string using simple string parsing
*/
private fun extractPermissionsFromJson(jsonString: String): List<String>? {
return try {
// Simple regex to find a permissions array
val permissionsRegex = """"permissions":\s*\[(.*?)\]""".toRegex()
val permissionsRegex = """"permissions":\s*\[(.*?)]""".toRegex()
val match = permissionsRegex.find(jsonString)
match?.let {
val permissionsContent = it.groupValues[1]
match?.let { matchResult ->
val permissionsContent = matchResult.groupValues[1]
if (permissionsContent.isBlank()) return emptyList()
// Extract individual permission strings
@@ -9,7 +9,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module
/**
* Koin module for core-auth: provides AuthTokenManager and binds it as TokenProvider for apiClient.
* Koin-Modul für core-auth: stellt AuthTokenManager bereit und bindet ihn als TokenProvider für apiClient.
*/
val authModule = module {
// Single in-memory token manager
@@ -26,9 +26,9 @@ val authModule = module {
// LoginViewModel
factory { LoginViewModel(get(), get(), get(named("apiClient"))) }
// Bridge to core network TokenProvider without adding a hard dependency there
// Brücke zum TokenProvider des Kernnetzwerks, ohne dort eine harte Abhängigkeit hinzuzufügen
single<TokenProvider> {
// We need to capture the AuthTokenManager instance to avoid issues with 'this' context in JS
// Wir müssen die AuthTokenManager-Instanz erfassen, um Probleme mit dem 'this'-Kontext in JavaScript zu vermeiden.
val tokenManager = get<AuthTokenManager>()
object : TokenProvider {
override fun getAccessToken(): String? {