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]

Reply via email to