Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements #106

Merged
merged 3 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package eu.europa.ec.eudi.verifier.endpoint

import arrow.core.*
import com.nimbusds.jose.EncryptionMethod
import com.nimbusds.jose.JWEAlgorithm
import com.nimbusds.jose.JWSAlgorithm
Expand All @@ -41,19 +42,28 @@ import eu.europa.ec.eudi.verifier.endpoint.port.out.cfg.CreateQueryWalletRespons
import eu.europa.ec.eudi.verifier.endpoint.port.out.cfg.GenerateResponseCode
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import org.slf4j.LoggerFactory
import org.springframework.boot.web.codec.CodecCustomizer
import org.springframework.context.support.beans
import org.springframework.core.env.Environment
import org.springframework.core.env.getProperty
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.core.io.FileSystemResource
import org.springframework.http.codec.json.KotlinSerializationJsonDecoder
import org.springframework.http.codec.json.KotlinSerializationJsonEncoder
import org.springframework.web.reactive.config.WebFluxConfigurer
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.reactive.CorsConfigurationSource
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.time.Clock
import java.time.Duration
import java.util.*

private val log = LoggerFactory.getLogger(VerifierApplication::class.java)

@OptIn(ExperimentalSerializationApi::class)
internal fun beans(clock: Clock) = beans {
//
// JOSE
Expand Down Expand Up @@ -142,18 +152,40 @@ internal fun beans(clock: Clock) = beans {
// Other
//
bean {
object : WebFluxConfigurer {
@OptIn(ExperimentalSerializationApi::class)
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
val json = Json {
explicitNulls = false
ignoreUnknownKeys = true
}
CodecCustomizer {
val json = Json {
explicitNulls = false
ignoreUnknownKeys = true
}

configurer.defaultCodecs().kotlinSerializationJsonDecoder(KotlinSerializationJsonDecoder(json))
configurer.defaultCodecs().kotlinSerializationJsonEncoder(KotlinSerializationJsonEncoder(json))
configurer.defaultCodecs().enableLoggingRequestDetails(true)
it.defaultCodecs().kotlinSerializationJsonDecoder(KotlinSerializationJsonDecoder(json))
it.defaultCodecs().kotlinSerializationJsonEncoder(KotlinSerializationJsonEncoder(json))
it.defaultCodecs().enableLoggingRequestDetails(true)
}
}
bean {
val http = ref<ServerHttpSecurity>()
http {
cors { // cross-origin resource sharing configuration
configurationSource = CorsConfigurationSource {
CorsConfiguration().apply {
fun getOptionalList(name: String): NonEmptyList<String>? =
env.getOptionalList(name = name, filter = { it.isNotBlank() }, transform = { it.trim() })

allowedOrigins = getOptionalList("cors.origins")
allowedOriginPatterns = getOptionalList("cors.originPatterns")
allowedMethods = getOptionalList("cors.methods")
run {
val headers = getOptionalList("cors.headers")
allowedHeaders = headers
exposedHeaders = headers
}
allowCredentials = env.getProperty<Boolean>("cors.credentials")
maxAge = env.getProperty<Long>("cors.maxAge")
}
}
}
csrf { disable() } // cross-site request forgery disabled
}
}
}
Expand All @@ -168,11 +200,27 @@ private enum class SigningKeyEnum {
LoadFromKeystore,
}

private const val keystoreDefaultLocation = "/keystore.jks"

private fun jarSigningConfig(environment: Environment, clock: Clock): SigningConfig {
val key = run {
fun loadFromKeystore(): JWK {
val keystoreResource =
DefaultResourceLoader().getResource((environment.getRequiredProperty("verifier.jar.signing.key.keystore")))
val keystoreResource = run {
val keystoreLocation = environment.getRequiredProperty("verifier.jar.signing.key.keystore")
log.info("Will try to load Keystore from: '{}'", keystoreLocation)
val keystoreResource = DefaultResourceLoader().getResource(keystoreLocation)
.some()
.filter { it.exists() }
.recover {
log.warn("Could not find Keystore at '{}'. Fallback to '{}'", keystoreLocation, keystoreDefaultLocation)
FileSystemResource(keystoreDefaultLocation)
.some()
.filter { it.exists() }
.bind()
}
.getOrNull()
checkNotNull(keystoreResource) { "Could not load Keystore either from '$keystoreLocation' or '$keystoreDefaultLocation'" }
}

val keystoreType =
environment.getProperty("verifier.jar.signing.key.keystore.type", KeyStore.getDefaultType())
Expand Down Expand Up @@ -331,3 +379,22 @@ private fun JWK.withCertificateChain(chain: List<X509Certificate>): JWK {
else -> error("Unexpected JWK type '${this.keyType.value}'/'${this.javaClass}'")
}
}

/**
* Gets the value of a property that contains a comma-separated list. A list is returned when it contains values.
*
* @receiver the configured Spring [Environment] from which to load the property
* @param name the property to load
* @param filter optional filter to apply to the list value
* @param transform optional mapping to apply to the list values
*/
private fun Environment.getOptionalList(
name: String,
filter: (String) -> Boolean = { true },
transform: (String) -> String = { it },
): NonEmptyList<String>? =
this.getProperty(name)
?.split(",")
?.filter { filter(it) }
?.map { transform(it) }
?.toNonEmptyListOrNull()
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@ import org.springframework.context.ApplicationContextInitializer
import org.springframework.context.support.BeanDefinitionDsl
import org.springframework.context.support.GenericApplicationContext
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.web.reactive.config.EnableWebFlux
import java.time.Clock

@EnableScheduling
@EnableWebFlux
@EnableWebFluxSecurity
@SpringBootApplication(proxyBeanMethods = false)
class VerifierApplication

internal fun BeanDefinitionDsl.initializer(): ApplicationContextInitializer<GenericApplicationContext> =
ApplicationContextInitializer<GenericApplicationContext> { initialize(it) }

fun main(args: Array<String>) {
runApplication<VerifierApplication>(*args) {
addInitializers(beans(Clock.systemDefaultZone()).initializer())
Expand Down

This file was deleted.