Skip to content

Commit

Permalink
CORE-8415 - Upgrade Javalin to version 6.2.0 (#6211)
Browse files Browse the repository at this point in the history
  • Loading branch information
filipesoliveira authored Jul 23, 2024
1 parent 9bfca23 commit f78fe82
Show file tree
Hide file tree
Showing 30 changed files with 218 additions and 214 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package net.corda.metrics.reader

import io.javalin.Javalin
import io.javalin.core.util.Header
import io.javalin.http.HandlerType
import io.javalin.http.Header
import org.slf4j.LoggerFactory
import java.nio.file.Files
import java.nio.file.Path
Expand All @@ -27,7 +27,7 @@ class MetricsReaderMain {
loadMeasurements(Paths.get(metricsFile))
server = Javalin.create()

server?.addHandler(HandlerType.GET, "/metrics") { context ->
server?.addHttpHandler(HandlerType.GET, "/metrics") { context ->
context.result(nextReading())
context.header(Header.CACHE_CONTROL, "no-cache")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package net.corda.applications.workers.workercommon

import com.fasterxml.jackson.databind.ObjectMapper
import io.javalin.core.util.Header
import io.javalin.http.Header
import net.corda.lifecycle.LifecycleStatus
import net.corda.lifecycle.registry.LifecycleRegistry
import net.corda.rest.ResponseCode
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package net.corda.applications.workers.workercommon

import io.javalin.core.util.Header
import io.javalin.http.Header
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
import io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics
Expand Down
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ commonsioVersion = "2.16.1"
guavaVersion = "33.2.1-jre"
hikariCpVersion = "5.1.0"
jacksonVersion = "2.17.2"
javalinVersion = "4.6.8"
javalinVersion = "6.2.0"
jcipAnnotationsVersion = "1.0_2"
# This version of Jetty must be the same major version as used by Javalin, please see above.
# Once Javalin version is upgraded to the latest, this override may be removed.
jettyVersion = "9.4.55.v20240627"
jettyVersion = "11.0.21"
micrometerVersion = "1.12.4"
nimbusVersion = "11.13"
kafkaClientVersion = "3.7.0_1"
Expand Down Expand Up @@ -66,7 +66,7 @@ assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "as
bouncycastle-pkix = { group = "org.bouncycastle", name = "bcpkix-jdk18on", version.ref = "bouncycastleVersion" }
bouncycastle-prov = { group = "org.bouncycastle", name = "bcprov-jdk18on", version.ref = "bouncycastleVersion" }
brave-context-slf4j = { group = "io.zipkin.brave", name = "brave-context-slf4j", version.ref = "braveVersion" }
brave-instrumentation-servlet = { group = "io.zipkin.brave", name = "brave-instrumentation-servlet", version.ref = "braveVersion" }
brave-instrumentation-servlet = { group = "io.zipkin.brave", name = "brave-instrumentation-servlet-jakarta", version.ref = "braveVersion" }
caffeine = { group = "com.github.ben-manes.caffeine", name = "caffeine", version.ref = "caffeineVersion" }
commonsio = { group = "commons-io", name = "commons-io", version.ref = "commonsioVersion" }
guava = { group = "com.google.guava", name = "guava", version.ref = "guavaVersion" }
Expand All @@ -83,7 +83,7 @@ jetty-server = { group = "org.eclipse.jetty", name = "jetty-server", version.ref
jetty-xml = { group = "org.eclipse.jetty", name = "jetty-xml", version.ref = "jettyVersion" }
jetty-websocket-servlet = { group = "org.eclipse.jetty.websocket", name = "websocket-servlet", version.ref = "jettyVersion" }
jetty-websocket-server = { group = "org.eclipse.jetty.websocket", name = "websocket-server", version.ref = "jettyVersion" }
jetty-websocket-client = { group = "org.eclipse.jetty.websocket", name = "websocket-client", version.ref = "jettyVersion" }
jetty-websocket-client = { group = "org.eclipse.jetty.websocket", name = "websocket-jetty-client", version.ref = "jettyVersion" }
jetty-http2-server = { group = "org.eclipse.jetty.http2", name = "http2-server", version.ref = "jettyVersion" }
junit = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junitVersion" }
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junitVersion" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class RevocableCertificateAuthorityImpl(
internal const val PATH = "/ocsp"
}
private val app = Javalin.create().start(port)
.addHandler(HandlerType.GET, "$PATH/*", OcspHandler())
.addHttpHandler(HandlerType.GET, "$PATH/*", OcspHandler())
private val revokedCertificates = ConcurrentHashMap.newKeySet<BigInteger>()
private val clock = Clock.systemUTC()

Expand All @@ -51,7 +51,7 @@ internal class RevocableCertificateAuthorityImpl(
}

override fun close() {
app.close()
app.stop()
}

private inner class OcspHandler : Handler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package net.corda.restclient.generated

import io.javalin.Javalin
import io.javalin.http.ContentType
import io.javalin.http.Header
import io.javalin.http.servlet.getBasicAuthCredentials
import net.corda.restclient.CordaRestClient
import net.corda.restclient.generated.infrastructure.ApiClient
import org.assertj.core.api.AssertionsForClassTypes.assertThat
Expand All @@ -22,12 +24,12 @@ class TestClientAuthHeader {
app = Javalin.create().start(0)
app.get("api/v5_3/mgm/1234/info") { ctx ->

assertThat(ctx.basicAuthCredentialsExist()).isTrue()
val basicAuthCredentials = getBasicAuthCredentials(ctx.header(Header.AUTHORIZATION))
assertThat(basicAuthCredentials).isNotNull()

// Respond with the credentials from the basic auth header
val (user, password) = ctx.basicAuthCredentials()
ctx.contentType(ContentType.APPLICATION_JSON)
.result("$user:$password")
.result("${basicAuthCredentials?.username}:${basicAuthCredentials?.password}")
}
}

Expand Down
7 changes: 1 addition & 6 deletions libs/rest/rest-server-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,11 @@ dependencies {
implementation project(":libs:rest:rest-server")
implementation "net.corda:corda-crypto"
implementation libs.javalin
constraints {
implementation(libs.bundles.jetty) {
because 'Javalin uses an older version of Jetty which is exposed to CVE-2023-26048 and CVE-2023-26049. ' +
'This might be resolved in the future versions of Javalin.'
}
}
implementation libs.nimbus.sdk
implementation libs.jcip.annotations
implementation libs.swagger.core
implementation libs.jetty.http2.server
implementation libs.jetty.websocket.client

implementation libs.jackson.module.kotlin
implementation project(":libs:rest:rest-common")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package net.corda.rest.server.impl

import io.javalin.core.util.Header
import io.javalin.http.Header
import net.corda.rest.server.config.models.RestServerSettings
import net.corda.rest.test.utils.WebRequest
import net.corda.rest.tools.HttpVerb
import net.corda.utilities.debug
import org.apache.http.HttpStatus
import org.assertj.core.api.Assertions.assertThat
import org.eclipse.jetty.client.HttpRequest
import org.eclipse.jetty.client.HttpResponse
import org.eclipse.jetty.http.HttpField
import org.eclipse.jetty.io.EofException
import org.eclipse.jetty.websocket.api.CloseStatus
import org.eclipse.jetty.websocket.api.Session
import org.eclipse.jetty.websocket.api.StatusCode
import org.eclipse.jetty.websocket.api.UpgradeRequest
import org.eclipse.jetty.websocket.api.UpgradeResponse
import org.eclipse.jetty.websocket.api.WebSocketAdapter
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest
import org.eclipse.jetty.websocket.client.NoOpEndpoint
import org.eclipse.jetty.websocket.client.JettyUpgradeListener
import org.eclipse.jetty.websocket.client.WebSocketClient
import org.eclipse.jetty.websocket.client.io.UpgradeListener
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.slf4j.Logger
Expand Down Expand Up @@ -50,7 +52,8 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {
fun `valid path returns 200 OK`() {
val getPathResponse = client.call(HttpVerb.GET, WebRequest<Any>("health/sanity"), userName, password)
assertEquals(HttpStatus.SC_OK, getPathResponse.responseStatus)
assertEquals("localhost", getPathResponse.headers[Header.ACCESS_CONTROL_ALLOW_ORIGIN])

assertEquals("http://localhost", getPathResponse.headers[Header.ACCESS_CONTROL_ALLOW_ORIGIN])
assertEquals("true", getPathResponse.headers[Header.ACCESS_CONTROL_ALLOW_CREDENTIALS])
assertEquals("no-cache", getPathResponse.headers[Header.CACHE_CONTROL])
}
Expand All @@ -65,7 +68,7 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {
val maximumDesiredCount = 100
val list = mutableListOf<String>()

val wsHandler = object : NoOpEndpoint() {
val wsHandler = object : WebSocketAdapter() {

override fun onWebSocketConnect(session: Session) {
log.info("onWebSocketConnect : $session")
Expand All @@ -78,6 +81,7 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {
}

override fun onWebSocketText(message: String) {
log.debug { "Receiving: $message" }
list.add(message)
if (list.size >= maximumDesiredCount) {
log.warn("Too many received!")
Expand All @@ -86,14 +90,14 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {
}
}

val upgradeListener = object : UpgradeListener {
override fun onHandshakeRequest(request: UpgradeRequest) {
val upgradeListener = object : JettyUpgradeListener {
override fun onHandshakeRequest(request: HttpRequest) {
val headerValue = toBasicAuthValue(userName, password)
log.info("Header value: $headerValue")
request.setHeader(Header.AUTHORIZATION, headerValue)
request.addHeader(HttpField(Header.AUTHORIZATION, headerValue))
}

override fun onHandshakeResponse(response: UpgradeResponse) {
override fun onHandshakeResponse(request: HttpRequest, response: HttpResponse) {
}
}

Expand Down Expand Up @@ -130,12 +134,12 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {

@Test
fun `check WebSocket wrong credentials connectivity`() {
val upgradeListener = object : UpgradeListener {
override fun onHandshakeRequest(request: UpgradeRequest) {
request.setHeader(Header.AUTHORIZATION, toBasicAuthValue("alienUser", "wrongPassword"))
val upgradeListener = object : JettyUpgradeListener {
override fun onHandshakeRequest(request: HttpRequest) {
request.addHeader(HttpField(Header.AUTHORIZATION, toBasicAuthValue("alienUser", "wrongPassword")))
}

override fun onHandshakeResponse(response: UpgradeResponse) {
override fun onHandshakeResponse(request: HttpRequest, response: HttpResponse) {
}
}

Expand All @@ -147,14 +151,14 @@ abstract class AbstractWebsocketTest : RestServerTestBase() {
performUnauthorizedTest(null)
}

private fun performUnauthorizedTest(upgradeListener: UpgradeListener?) {
private fun performUnauthorizedTest(upgradeListener: JettyUpgradeListener?) {
val wsClient = createWsClient()
wsClient.start()

val latch = CountDownLatch(2)
var closeStatus: CloseStatus? = null

val wsHandler = object : NoOpEndpoint() {
val wsHandler = object : WebSocketAdapter() {

override fun onWebSocketConnect(session: Session) {
log.info("onWebSocketConnect : $session")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import net.corda.rest.test.utils.TestHttpClientUnirestImpl
import net.corda.rest.test.utils.multipartDir
import net.corda.utilities.NetworkHostAndPort
import org.eclipse.jetty.client.HttpClient
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic
import org.eclipse.jetty.io.ClientConnector
import org.eclipse.jetty.util.ssl.SslContextFactory
import org.eclipse.jetty.websocket.client.WebSocketClient
import org.junit.jupiter.api.AfterAll
Expand Down Expand Up @@ -53,6 +55,7 @@ class RestServerHTTPSWebsocketTest : AbstractWebsocketTest() {
multipartDir,
true
).apply { start() }

client = TestHttpClientUnirestImpl(
"https://${restServerSettings.address.host}:${server.port}" +
"/${restServerSettings.context.basePath}/${apiVersion.versionPath}/",
Expand All @@ -70,7 +73,12 @@ class RestServerHTTPSWebsocketTest : AbstractWebsocketTest() {
}
}

override fun createWsClient() = WebSocketClient(HttpClient(SslContextFactory.Client(true)))
override fun createWsClient(): WebSocketClient {
val clientConnector = ClientConnector().apply {
sslContextFactory = SslContextFactory.Client(true)
}
return WebSocketClient(HttpClient(HttpClientTransportDynamic(clientConnector)))
}

override val wsProtocol = "wss"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ class RestServerOpenApiTest : RestServerTestBase() {
fun `GET swagger UI should return html with reference to swagger json`() {
val apiSpec = client.call(GET, WebRequest<Any>("swagger"))
assertEquals(HttpStatus.SC_OK, apiSpec.responseStatus)
assertEquals("text/html", apiSpec.headers["Content-Type"])
assertEquals("text/html;charset=utf-8", apiSpec.headers["Content-Type"])
val expected = """url: "/${context.basePath}/${apiVersion.versionPath}/swagger.json""""
assertTrue(apiSpec.body!!.contains(expected))
}
Expand All @@ -505,7 +505,7 @@ class RestServerOpenApiTest : RestServerTestBase() {
fun `GET swagger UI with trailing slash in path should return html with reference to swagger json without trailing slash`() {
val apiSpec = client.call(GET, WebRequest<Any>("swagger/"))
assertEquals(HttpStatus.SC_OK, apiSpec.responseStatus)
assertEquals("text/html", apiSpec.headers["Content-Type"])
assertEquals("text/html;charset=utf-8", apiSpec.headers["Content-Type"])
val expected = """url: "/${context.basePath}/${apiVersion.versionPath}/swagger.json""""
assertTrue(apiSpec.body!!.contains(expected))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package net.corda.rest.server.impl

import com.google.gson.Gson
import io.javalin.core.util.Header.ACCESS_CONTROL_ALLOW_CREDENTIALS
import io.javalin.core.util.Header.ACCESS_CONTROL_ALLOW_ORIGIN
import io.javalin.core.util.Header.CACHE_CONTROL
import io.javalin.core.util.Header.WWW_AUTHENTICATE
import io.javalin.http.Header.ACCESS_CONTROL_ALLOW_CREDENTIALS
import io.javalin.http.Header.ACCESS_CONTROL_ALLOW_ORIGIN
import io.javalin.http.Header.CACHE_CONTROL
import io.javalin.http.Header.WWW_AUTHENTICATE
import net.corda.rest.annotations.RestApiVersion
import net.corda.rest.server.apigen.test.TestJavaPrimitivesRestResourceImpl
import net.corda.rest.server.config.models.RestServerSettings
Expand Down Expand Up @@ -107,8 +107,9 @@ class RestServerRequestsTest : RestServerTestBase() {
userName,
password
)

assertEquals(HttpStatus.SC_OK, getPathResponse.responseStatus)
assertEquals("localhost", getPathResponse.headers[ACCESS_CONTROL_ALLOW_ORIGIN])
assertEquals("http://localhost", getPathResponse.headers[ACCESS_CONTROL_ALLOW_ORIGIN])
assertEquals("true", getPathResponse.headers[ACCESS_CONTROL_ALLOW_CREDENTIALS])
assertEquals("no-cache", getPathResponse.headers[CACHE_CONTROL])
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package net.corda.rest.server.impl.context

import io.javalin.core.util.Header
import io.javalin.http.Context
import io.javalin.http.Header
import io.javalin.http.UploadedFile
import io.javalin.plugin.json.JsonMapper
import io.javalin.plugin.json.jsonMapper
import io.javalin.json.JsonMapper
import net.corda.data.rest.PasswordExpiryStatus
import net.corda.rest.server.impl.security.RestAuthenticationProvider

Expand All @@ -14,7 +13,7 @@ import net.corda.rest.server.impl.security.RestAuthenticationProvider
internal class ClientHttpRequestContext(private val ctx: Context) : ClientRequestContext {

override val method: String
get() = ctx.method()
get() = ctx.method().name

override fun header(header: String): String? = ctx.header(header)

Expand Down Expand Up @@ -60,12 +59,12 @@ internal class ClientHttpRequestContext(private val ctx: Context) : ClientReques
}

override fun addPasswordExpiryHeader(expiryStatus: PasswordExpiryStatus) {
ctx.res.addHeader(Header.WARNING, "199 - PasswordExpiryStatus is $expiryStatus")
ctx.res().addHeader(Header.WARNING, "199 - PasswordExpiryStatus is $expiryStatus")
}

private fun addHeaderValues(values: Iterable<String>) {
values.forEach {
ctx.res.addHeader(Header.WWW_AUTHENTICATE, it)
ctx.res().addHeader(Header.WWW_AUTHENTICATE, it)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package net.corda.rest.server.impl.context

import io.javalin.core.security.BasicAuthCredentials
import io.javalin.core.util.Header
import io.javalin.http.Header
import io.javalin.http.UploadedFile
import io.javalin.http.util.ContextUtil
import io.javalin.plugin.json.JsonMapper
import io.javalin.http.servlet.getBasicAuthCredentials
import io.javalin.json.JsonMapper
import io.javalin.security.BasicAuthCredentials
import net.corda.data.rest.PasswordExpiryStatus
import net.corda.rest.server.impl.security.RestAuthenticationProvider

Expand Down Expand Up @@ -101,13 +101,16 @@ interface ClientRequestContext {
* Returns a Boolean which is true if there is an Authorization header with
* Basic auth credentials. Returns false otherwise.
*/
fun basicAuthCredentialsExist(): Boolean = ContextUtil.hasBasicAuthCredentials(header(Header.AUTHORIZATION))
fun basicAuthCredentialsExist(): Boolean = getBasicAuthCredentials() != null

/**
* Gets basic-auth credentials from the request, or throws.
*
* Returns a wrapper object [BasicAuthCredentials] which contains the
* Base64 decoded username and password from the Authorization header.
*/
fun basicAuthCredentials(): BasicAuthCredentials = ContextUtil.getBasicAuthCredentials(header(Header.AUTHORIZATION))
fun basicAuthCredentials(): BasicAuthCredentials = getBasicAuthCredentials()!!

private fun getBasicAuthCredentials() =
getBasicAuthCredentials(header(Header.AUTHORIZATION))
}
Loading

0 comments on commit f78fe82

Please sign in to comment.