Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 28cd5d8afa | |||
| bafb63dc16 |
+6
-4
@@ -64,16 +64,18 @@ class SecurityConfig(
|
|||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
try {
|
|
||||||
if (jwkSetUri.isBlank()) {
|
if (jwkSetUri.isBlank()) {
|
||||||
throw IllegalArgumentException("JWK Set URI is missing")
|
logger.error("JWK Set URI is missing – all authenticated requests will be rejected.")
|
||||||
|
return Mono.error(org.springframework.security.oauth2.jwt.BadJwtException("Identity Provider not configured"))
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
logger.info("Attempting to initialize JWT Decoder with URI: {}", jwkSetUri)
|
logger.info("Attempting to initialize JWT Decoder with URI: {}", jwkSetUri)
|
||||||
delegate = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build()
|
delegate = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build()
|
||||||
logger.info("JWT Decoder successfully initialized.")
|
logger.info("JWT Decoder successfully initialized.")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.warn("Could not initialize JWT Decoder: {}", e.message)
|
logger.warn("Could not initialize JWT Decoder: {}", e.message)
|
||||||
return Mono.error(IllegalStateException("Identity Provider unavailable"))
|
// Throw BadJwtException so Spring Security returns 401, not 500 or passthrough
|
||||||
|
return Mono.error(org.springframework.security.oauth2.jwt.BadJwtException("Identity Provider unavailable: ${e.message}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +135,7 @@ data class GatewaySecurityProperties(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class CorsProperties(
|
data class CorsProperties(
|
||||||
val allowedOriginPatterns: Set<String> = setOf("http://localhost:*", "https://*.meldestelle.at"),
|
val allowedOriginPatterns: Set<String> = setOf("http://localhost:*", "https://*.meldestelle.at", "https://*.mo-code.at"),
|
||||||
val allowedMethods: Set<String> = setOf("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"),
|
val allowedMethods: Set<String> = setOf("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"),
|
||||||
val allowedHeaders: Set<String> = setOf("*"),
|
val allowedHeaders: Set<String> = setOf("*"),
|
||||||
val exposedHeaders: Set<String> = setOf("X-Correlation-ID"),
|
val exposedHeaders: Set<String> = setOf("X-Correlation-ID"),
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ Setze folgende Variablen in Postman (Environment "Local Docker"):
|
|||||||
* `client_id`: `postman-client`
|
* `client_id`: `postman-client`
|
||||||
* `client_secret`: `postman-secret-123`
|
* `client_secret`: `postman-secret-123`
|
||||||
* `username`: `admin`
|
* `username`: `admin`
|
||||||
* `password`: `Change_Me_In_Production!`
|
* `password`: `Admin#1234`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+4
-6
@@ -22,10 +22,8 @@ import at.mocode.frontend.core.designsystem.theme.Dimens
|
|||||||
@Composable
|
@Composable
|
||||||
fun PingScreen(
|
fun PingScreen(
|
||||||
viewModel: PingViewModel,
|
viewModel: PingViewModel,
|
||||||
onBack: () -> Unit = {},
|
onBack: () -> Unit = {}
|
||||||
isAuthenticated: Boolean = false
|
|
||||||
) {
|
) {
|
||||||
viewModel.isAuthenticated = isAuthenticated
|
|
||||||
val uiState = viewModel.uiState
|
val uiState = viewModel.uiState
|
||||||
|
|
||||||
// Wir nutzen jetzt das globale Theme (Hintergrund kommt vom Theme)
|
// Wir nutzen jetzt das globale Theme (Hintergrund kommt vom Theme)
|
||||||
@@ -53,7 +51,7 @@ fun PingScreen(
|
|||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.padding(end = Dimens.SpacingS)
|
.padding(end = Dimens.SpacingS)
|
||||||
) {
|
) {
|
||||||
ActionToolbar(viewModel, isAuthenticated)
|
ActionToolbar(viewModel)
|
||||||
Spacer(Modifier.height(Dimens.SpacingS))
|
Spacer(Modifier.height(Dimens.SpacingS))
|
||||||
StatusGrid(uiState)
|
StatusGrid(uiState)
|
||||||
}
|
}
|
||||||
@@ -133,7 +131,7 @@ private fun StatusBadge(text: String, color: Color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ActionToolbar(viewModel: PingViewModel, isAuthenticated: Boolean) {
|
private fun ActionToolbar(viewModel: PingViewModel) {
|
||||||
// Wrap buttons to avoid overflow on small screens
|
// Wrap buttons to avoid overflow on small screens
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
@@ -141,7 +139,7 @@ private fun ActionToolbar(viewModel: PingViewModel, isAuthenticated: Boolean) {
|
|||||||
) {
|
) {
|
||||||
DenseButton(text = "Simple", onClick = { viewModel.performSimplePing() })
|
DenseButton(text = "Simple", onClick = { viewModel.performSimplePing() })
|
||||||
DenseButton(text = "Enhanced", onClick = { viewModel.performEnhancedPing() })
|
DenseButton(text = "Enhanced", onClick = { viewModel.performEnhancedPing() })
|
||||||
DenseButton(text = "Secure", onClick = { viewModel.performSecurePing() }, enabled = isAuthenticated)
|
DenseButton(text = "Secure", onClick = { viewModel.performSecurePing() })
|
||||||
DenseButton(text = "Health", onClick = { viewModel.performHealthCheck() })
|
DenseButton(text = "Health", onClick = { viewModel.performHealthCheck() })
|
||||||
DenseButton(
|
DenseButton(
|
||||||
text = "Sync",
|
text = "Sync",
|
||||||
|
|||||||
-6
@@ -41,8 +41,6 @@ class PingViewModel(
|
|||||||
var uiState by mutableStateOf(PingUiState())
|
var uiState by mutableStateOf(PingUiState())
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var isAuthenticated: Boolean = false
|
|
||||||
|
|
||||||
private fun addLog(source: String, message: String, isError: Boolean = false) {
|
private fun addLog(source: String, message: String, isError: Boolean = false) {
|
||||||
val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
|
val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
|
||||||
val timeString = "${now.hour.toString().padStart(2, '0')}:${now.minute.toString().padStart(2, '0')}:${
|
val timeString = "${now.hour.toString().padStart(2, '0')}:${now.minute.toString().padStart(2, '0')}:${
|
||||||
@@ -110,10 +108,6 @@ class PingViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun performSecurePing() {
|
fun performSecurePing() {
|
||||||
if (!isAuthenticated) {
|
|
||||||
addLog("SecurePing", "Not authenticated – request blocked.", isError = true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
uiState = uiState.copy(isLoading = true, errorMessage = null)
|
||||||
addLog("SecurePing", "Sending authenticated request...")
|
addLog("SecurePing", "Sending authenticated request...")
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ fun MainApp() {
|
|||||||
// Delta-Sync blueprint: resolve the Ping feature view model via Koin.
|
// Delta-Sync blueprint: resolve the Ping feature view model via Koin.
|
||||||
val pingViewModel: PingViewModel = koinViewModel()
|
val pingViewModel: PingViewModel = koinViewModel()
|
||||||
val loginViewModel: LoginViewModel = koinViewModel()
|
val loginViewModel: LoginViewModel = koinViewModel()
|
||||||
val authState by authTokenManager.authState.collectAsState()
|
|
||||||
|
|
||||||
when (currentScreen) {
|
when (currentScreen) {
|
||||||
is AppScreen.Landing -> LandingScreen(
|
is AppScreen.Landing -> LandingScreen(
|
||||||
@@ -59,8 +58,7 @@ fun MainApp() {
|
|||||||
|
|
||||||
is AppScreen.Ping -> PingScreen(
|
is AppScreen.Ping -> PingScreen(
|
||||||
viewModel = pingViewModel,
|
viewModel = pingViewModel,
|
||||||
onBack = { navigationPort.navigateToScreen(AppScreen.Home) },
|
onBack = { navigationPort.navigateToScreen(AppScreen.Home) } // Navigate back to Home
|
||||||
isAuthenticated = authState.isAuthenticated
|
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppScreen.Profile -> AuthStatusScreen(
|
is AppScreen.Profile -> AuthStatusScreen(
|
||||||
|
|||||||
Reference in New Issue
Block a user