This is an automated email from the ASF dual-hosted git repository.
toulmean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
The following commit(s) were added to refs/heads/main by this push:
new e6b9d52 Add JSON-RPC method allowlist
new 9716333 Merge pull request #319 from atoulme/add_allowlist
e6b9d52 is described below
commit e6b9d52aeeb050f4f1e35c3217dd2dd0dddd90a9
Author: Antoine Toulme <[email protected]>
AuthorDate: Sun Jul 25 00:35:09 2021 -0700
Add JSON-RPC method allowlist
---
.../org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt | 11 ++++++++++-
.../org/apache/tuweni/jsonrpc/app/JSONRPCConfig.kt | 3 +++
.../tuweni/jsonrpc/methods/MethodsHandler.kt | 17 +++++++++++++++++
.../tuweni/jsonrpc/methods/MethodsHandlerTest.kt | 22 ++++++++++++++++++++++
4 files changed, 52 insertions(+), 1 deletion(-)
diff --git
a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt
b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt
index f7db85f..971ad9a 100644
--- a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt
+++ b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt
@@ -22,6 +22,8 @@ import kotlinx.coroutines.runBlocking
import org.apache.tuweni.eth.JSONRPCRequest
import org.apache.tuweni.eth.JSONRPCResponse
import org.apache.tuweni.jsonrpc.JSONRPCServer
+import org.apache.tuweni.jsonrpc.methods.MeteredHandler
+import org.apache.tuweni.jsonrpc.methods.MethodAllowListHandler
import org.apache.tuweni.metrics.MetricsService
import org.apache.tuweni.net.tls.VertxTrustOptions
import org.bouncycastle.jce.provider.BouncyCastleProvider
@@ -70,6 +72,13 @@ class JSONRPCApplication(
fun run() {
// TODO allow more options such as allowlist of certificates, enforce
client authentication.
val trustOptions =
VertxTrustOptions.recordClientFingerprints(config.clientFingerprintsFile())
+
+ val allowListHandler = MethodAllowListHandler(config.allowedMethods(),
this::handleRequest)
+
+ val meter = metricsService.meterSdkProvider.get("jsonrpc")
+ val successCounter = meter.longCounterBuilder("success").build()
+ val failureCounter = meter.longCounterBuilder("failure").build()
+ val handler = MeteredHandler(successCounter, failureCounter,
allowListHandler::handleRequest)
val server = JSONRPCServer(
vertx, config.port(), config.networkInterface(),
config.ssl(),
@@ -78,7 +87,7 @@ class JSONRPCApplication(
config.basicAuthUsername(),
config.basicAuthPassword(),
config.basicAuthRealm(),
- this::handleRequest,
+ methodHandler = handler::handleRequest,
Executors.newFixedThreadPool(
config.numberOfThreads()
) {
diff --git
a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCConfig.kt
b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCConfig.kt
index 91a9b96..8977dd3 100644
--- a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCConfig.kt
+++ b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCConfig.kt
@@ -21,6 +21,7 @@ import org.apache.tuweni.config.PropertyValidator
import org.apache.tuweni.config.SchemaBuilder
import java.nio.file.Path
import java.nio.file.Paths
+import java.util.Collections
/**
* Configuration of the JSON-RPC server as a TOML-based file.
@@ -43,6 +44,7 @@ class JSONRPCConfig(val filePath: Path) {
.addString("basicAuthUsername", null, "HTTP Basic Auth username", null)
.addString("basicAuthPassword", null, "HTTP Basic Auth password", null)
.addString("basicAuthRealm", null, "HTTP Basic Auth realm", null)
+ .addListOfString("allowedMethods", Collections.emptyList(), "Allowed
JSON-RPC methods", null)
.toSchema()
}
@@ -62,4 +64,5 @@ class JSONRPCConfig(val filePath: Path) {
fun basicAuthUsername() = config.getString("basicAuthUsername")
fun basicAuthPassword() = config.getString("basicAuthPassword")
fun basicAuthRealm() = config.getString("basicAuthRealm")
+ fun allowedMethods() = config.getListOfString("allowedMethods")
}
diff --git
a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt
b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt
index 0f9e5c6..ea50ed5 100644
---
a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt
+++
b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt
@@ -48,6 +48,23 @@ class MeteredHandler(private val successCounter:
LongCounter, private val failCo
}
}
+class MethodAllowListHandler(private val allowedMethods: List<String>, private
val delegateHandler: (JSONRPCRequest) -> JSONRPCResponse) {
+
+ fun handleRequest(request: JSONRPCRequest): JSONRPCResponse {
+ var found = false
+ for (method in allowedMethods) {
+ if (request.method.startsWith(method)) {
+ found = true
+ break
+ }
+ }
+ if (!found) {
+ return JSONRPCResponse(request.id, null, mapOf(Pair("code", -32604),
Pair("message", "Method not enabled")))
+ }
+ return delegateHandler(request)
+ }
+}
+
// TODO DelegateHandler - choose from a number of handlers to see which to
delegate to.
// TODO FilterHandler - filter incoming requests per allowlist
// TODO CachingHandler - cache some incoming requests
diff --git
a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt
b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt
index 82fd680..fe0a2a2 100644
---
a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt
+++
b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt
@@ -25,6 +25,8 @@ import org.apache.tuweni.eth.JSONRPCResponse
import org.apache.tuweni.eth.methodNotFound
import org.apache.tuweni.junit.BouncyCastleExtension
import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertNotNull
+import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.util.Collections
@@ -100,3 +102,23 @@ class MethodsHandlerTest {
assertEquals(1L, metricValue)
}
}
+
+class MethodAllowListHandlerTest {
+
+ @Test
+ fun testAllowedMethod() {
+ val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(1,
"foo") }
+ val resp = filter.handleRequest(JSONRPCRequest(1, "eth_client",
emptyArray()))
+ assertNull(resp.error)
+ }
+
+ @Test
+ fun testForbiddenMethod() {
+ val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(1,
"foo") }
+ val resp = filter.handleRequest(JSONRPCRequest(1, "foo_client",
emptyArray()))
+ assertNotNull(resp.error)
+ val respContents = resp.error as Map<*, *>
+ assertEquals(-32604, respContents["code"])
+ assertEquals("Method not enabled", respContents["message"])
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]