Add bearer token authorization for upstreams (#878)
This commit is contained in:
@@ -849,6 +849,18 @@ rpc:
|
|||||||
password: "${ETH_PASSWORD}"
|
password: "${ETH_PASSWORD}"
|
||||||
----
|
----
|
||||||
|
|
||||||
|
| `rpc.bearer-auth` + `rpc.bearer-auth.token`
|
||||||
|
a| HTTP Bearer token authorization (`Authorization: Bearer <token>` header), if required by the remote server. +
|
||||||
|
Cannot be used together with `basic-auth`.
|
||||||
|
Value can also reference env variables, for example:
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
rpc:
|
||||||
|
url: "https://ethereum.com:8545"
|
||||||
|
bearer-auth:
|
||||||
|
token: "${ETH_TOKEN}"
|
||||||
|
----
|
||||||
|
|
||||||
| `ws.url`
|
| `ws.url`
|
||||||
| WebSocket URL to connect to.
|
| WebSocket URL to connect to.
|
||||||
Optional, but optimizes performance if it's available.
|
Optional, but optimizes performance if it's available.
|
||||||
@@ -859,6 +871,9 @@ Optional, but optimizes performance if it's available.
|
|||||||
| `ws.basic-auth` + ...
|
| `ws.basic-auth` + ...
|
||||||
| WebSocket Basic Auth configuration, if required by the remote server
|
| WebSocket Basic Auth configuration, if required by the remote server
|
||||||
|
|
||||||
|
| `ws.bearer-auth` + `ws.bearer-auth.token`
|
||||||
|
| WebSocket Bearer token authorization, if required by the remote server
|
||||||
|
|
||||||
| `ws.frameSize`
|
| `ws.frameSize`
|
||||||
| WebSocket frame size limit.
|
| WebSocket frame size limit.
|
||||||
Ex `1kb`, `1024` (same as `1kb), `2mb`, etc.
|
Ex `1kb`, `1024` (same as `1kb), `2mb`, etc.
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class AuthConfig {
|
|||||||
val password: String,
|
val password: String,
|
||||||
) : ClientAuth()
|
) : ClientAuth()
|
||||||
|
|
||||||
|
class ClientBearerAuth(
|
||||||
|
val token: String,
|
||||||
|
) : ClientAuth()
|
||||||
|
|
||||||
class ClientTlsAuth(
|
class ClientTlsAuth(
|
||||||
var ca: String? = null,
|
var ca: String? = null,
|
||||||
var certificate: String? = null,
|
var certificate: String? = null,
|
||||||
|
|||||||
@@ -40,6 +40,18 @@ class AuthConfigReader : YamlConfigReader<AuthConfig>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readClientBearerAuth(node: MappingNode?): AuthConfig.ClientBearerAuth? {
|
||||||
|
return getMapping(node, "bearer-auth")?.let { authNode ->
|
||||||
|
val token = getValueAsString(authNode, "token")
|
||||||
|
if (token != null) {
|
||||||
|
AuthConfig.ClientBearerAuth(token)
|
||||||
|
} else {
|
||||||
|
log.warn("Bearer auth is not fully configured, token is required")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun readClientTls(node: MappingNode?): AuthConfig.ClientTlsAuth? {
|
fun readClientTls(node: MappingNode?): AuthConfig.ClientTlsAuth? {
|
||||||
return getMapping(node, "tls")?.let { authNode ->
|
return getMapping(node, "tls")?.let { authNode ->
|
||||||
val auth = AuthConfig.ClientTlsAuth()
|
val auth = AuthConfig.ClientTlsAuth()
|
||||||
|
|||||||
@@ -136,12 +136,14 @@ data class UpstreamsConfig(
|
|||||||
constructor(url: URI) : this(url, DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
|
constructor(url: URI) : this(url, DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
|
||||||
|
|
||||||
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
|
var bearerAuth: AuthConfig.ClientBearerAuth? = null
|
||||||
var tls: AuthConfig.ClientTlsAuth? = null
|
var tls: AuthConfig.ClientTlsAuth? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
data class WsEndpoint(val url: URI) {
|
data class WsEndpoint(val url: URI) {
|
||||||
var origin: URI? = null
|
var origin: URI? = null
|
||||||
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
|
var bearerAuth: AuthConfig.ClientBearerAuth? = null
|
||||||
var frameSize: Int? = null
|
var frameSize: Int? = null
|
||||||
var msgSize: Int? = null
|
var msgSize: Int? = null
|
||||||
var connections: Int = 1
|
var connections: Int = 1
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ class UpstreamsConfigReader(
|
|||||||
getValueAsString(node, "url")?.let { url ->
|
getValueAsString(node, "url")?.let { url ->
|
||||||
val http = UpstreamsConfig.HttpEndpoint(URI(url), DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
|
val http = UpstreamsConfig.HttpEndpoint(URI(url), DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
|
||||||
http.basicAuth = authConfigReader.readClientBasicAuth(node)
|
http.basicAuth = authConfigReader.readClientBasicAuth(node)
|
||||||
|
http.bearerAuth = readBearerAuth(node, http.basicAuth, url)
|
||||||
http.tls = authConfigReader.readClientTls(node)
|
http.tls = authConfigReader.readClientTls(node)
|
||||||
connection.esplora = http
|
connection.esplora = http
|
||||||
}
|
}
|
||||||
@@ -173,6 +174,19 @@ class UpstreamsConfigReader(
|
|||||||
return connection
|
return connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun readBearerAuth(
|
||||||
|
node: MappingNode?,
|
||||||
|
basicAuth: AuthConfig.ClientBasicAuth?,
|
||||||
|
url: String,
|
||||||
|
): AuthConfig.ClientBearerAuth? {
|
||||||
|
val bearerAuth = authConfigReader.readClientBearerAuth(node)
|
||||||
|
if (bearerAuth != null && basicAuth != null) {
|
||||||
|
log.warn("Both basic-auth and bearer-auth are configured for $url, basic-auth is used")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return bearerAuth
|
||||||
|
}
|
||||||
|
|
||||||
private fun readRpcConfig(connConfigNode: MappingNode): UpstreamsConfig.HttpEndpoint? {
|
private fun readRpcConfig(connConfigNode: MappingNode): UpstreamsConfig.HttpEndpoint? {
|
||||||
return getMapping(connConfigNode, "rpc")?.let { node ->
|
return getMapping(connConfigNode, "rpc")?.let { node ->
|
||||||
val maxConnections = getValueAsInt(node, "max-connections") ?: DEFAULT_MAX_CONNECTIONS
|
val maxConnections = getValueAsInt(node, "max-connections") ?: DEFAULT_MAX_CONNECTIONS
|
||||||
@@ -181,6 +195,7 @@ class UpstreamsConfigReader(
|
|||||||
getValueAsString(node, "url")?.let { url ->
|
getValueAsString(node, "url")?.let { url ->
|
||||||
val http = UpstreamsConfig.HttpEndpoint(URI(url), maxConnections, queueSize)
|
val http = UpstreamsConfig.HttpEndpoint(URI(url), maxConnections, queueSize)
|
||||||
http.basicAuth = authConfigReader.readClientBasicAuth(node)
|
http.basicAuth = authConfigReader.readClientBasicAuth(node)
|
||||||
|
http.bearerAuth = readBearerAuth(node, http.basicAuth, url)
|
||||||
http.tls = authConfigReader.readClientTls(node)
|
http.tls = authConfigReader.readClientTls(node)
|
||||||
http
|
http
|
||||||
}
|
}
|
||||||
@@ -224,6 +239,7 @@ class UpstreamsConfigReader(
|
|||||||
ws.origin = URI(origin)
|
ws.origin = URI(origin)
|
||||||
}
|
}
|
||||||
ws.basicAuth = authConfigReader.readClientBasicAuth(node)
|
ws.basicAuth = authConfigReader.readClientBasicAuth(node)
|
||||||
|
ws.bearerAuth = readBearerAuth(node, ws.basicAuth, url)
|
||||||
|
|
||||||
getValueAsBytes(node, "frameSize")?.let {
|
getValueAsBytes(node, "frameSize")?.let {
|
||||||
if (it < 65_535) {
|
if (it < 65_535) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class BitcoinUpstreamCreator(
|
|||||||
fileResolver.resolve(ca).readBytes()
|
fileResolver.resolve(ca).readBytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EsploraClient(endpoint.url, endpoint.basicAuth, tls)
|
EsploraClient(endpoint.url, endpoint.basicAuth, tls, endpoint.bearerAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
val extractBlock = ExtractBlock()
|
val extractBlock = ExtractBlock()
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ open class GenericConnectorFactoryCreator(
|
|||||||
monitoringCfg.nettyMetricsConfig.enabled,
|
monitoringCfg.nettyMetricsConfig.enabled,
|
||||||
httpScheduler,
|
httpScheduler,
|
||||||
customHeaders,
|
customHeaders,
|
||||||
|
conn.bearerAuth,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,6 +107,7 @@ open class GenericConnectorFactoryCreator(
|
|||||||
).apply {
|
).apply {
|
||||||
config = endpoint
|
config = endpoint
|
||||||
basicAuth = endpoint.basicAuth
|
basicAuth = endpoint.basicAuth
|
||||||
|
bearerAuth = endpoint.bearerAuth
|
||||||
this.customHeaders = customHeaders
|
this.customHeaders = customHeaders
|
||||||
}
|
}
|
||||||
val wsApi = WsConnectionPoolFactory(
|
val wsApi = WsConnectionPoolFactory(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class BasicHttpFactory(
|
|||||||
private val nettyMetricsEnabled: Boolean,
|
private val nettyMetricsEnabled: Boolean,
|
||||||
private val httpScheduler: Scheduler,
|
private val httpScheduler: Scheduler,
|
||||||
private val customHeaders: Map<String, String> = emptyMap(),
|
private val customHeaders: Map<String, String> = emptyMap(),
|
||||||
|
private val bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
) : HttpFactory {
|
) : HttpFactory {
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
private val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@@ -50,8 +51,8 @@ class BasicHttpFactory(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (chain.type.apiType == ApiType.REST) {
|
if (chain.type.apiType == ApiType.REST) {
|
||||||
return RestHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, chain, basicAuth, tls, customHeaders)
|
return RestHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, chain, basicAuth, tls, customHeaders, bearerAuth)
|
||||||
}
|
}
|
||||||
return JsonRpcHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, basicAuth, tls, customHeaders)
|
return JsonRpcHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, basicAuth, tls, customHeaders, bearerAuth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ abstract class HttpReader(
|
|||||||
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
||||||
tlsCAAuth: ByteArray? = null,
|
tlsCAAuth: ByteArray? = null,
|
||||||
customHeaders: Map<String, String> = emptyMap(),
|
customHeaders: Map<String, String> = emptyMap(),
|
||||||
|
bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
) : ChainReader {
|
) : ChainReader {
|
||||||
|
|
||||||
constructor() : this("", 1500, 1000, null)
|
constructor() : this("", 1500, 1000, null)
|
||||||
@@ -66,6 +67,13 @@ abstract class HttpReader(
|
|||||||
build = build.headers(headers)
|
build = build.headers(headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (basicAuth == null) {
|
||||||
|
bearerAuth?.let { auth ->
|
||||||
|
val headers = Consumer { h: HttpHeaders -> h.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}") }
|
||||||
|
build = build.headers(headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (customHeaders.isNotEmpty()) {
|
if (customHeaders.isNotEmpty()) {
|
||||||
val headers = Consumer { h: HttpHeaders ->
|
val headers = Consumer { h: HttpHeaders ->
|
||||||
customHeaders.forEach { (key, value) ->
|
customHeaders.forEach { (key, value) ->
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ import java.security.cert.X509Certificate
|
|||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
class EsploraClient(
|
class EsploraClient @JvmOverloads constructor(
|
||||||
private val url: URI,
|
private val url: URI,
|
||||||
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
||||||
tlsCAAuth: ByteArray? = null,
|
tlsCAAuth: ByteArray? = null,
|
||||||
|
bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -62,6 +63,13 @@ class EsploraClient(
|
|||||||
build = build.headers(headers)
|
build = build.headers(headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (basicAuth == null) {
|
||||||
|
bearerAuth?.let { auth ->
|
||||||
|
val headers = Consumer { h: HttpHeaders -> h.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}") }
|
||||||
|
build = build.headers(headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tlsCAAuth?.let { auth ->
|
tlsCAAuth?.let { auth ->
|
||||||
val cf = CertificateFactory.getInstance("X.509")
|
val cf = CertificateFactory.getInstance("X.509")
|
||||||
val cert = cf.generateCertificate(ByteArrayInputStream(auth)) as X509Certificate
|
val cert = cf.generateCertificate(ByteArrayInputStream(auth)) as X509Certificate
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ open class WsConnectionFactory(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
|
var bearerAuth: AuthConfig.ClientBearerAuth? = null
|
||||||
var config: UpstreamsConfig.WsEndpoint? = null
|
var config: UpstreamsConfig.WsEndpoint? = null
|
||||||
var customHeaders: Map<String, String> = emptyMap()
|
var customHeaders: Map<String, String> = emptyMap()
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ open class WsConnectionFactory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
open fun createWsConnection(connIndex: Int = 0): WsConnection =
|
open fun createWsConnection(connIndex: Int = 0): WsConnection =
|
||||||
WsConnectionImpl(uri, origin, basicAuth, metrics(connIndex), scheduler, eventsScheduler, customHeaders).also { ws ->
|
WsConnectionImpl(uri, origin, basicAuth, metrics(connIndex), scheduler, eventsScheduler, customHeaders, bearerAuth).also { ws ->
|
||||||
config?.frameSize?.let {
|
config?.frameSize?.let {
|
||||||
ws.frameSize = it
|
ws.frameSize = it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ open class WsConnectionImpl(
|
|||||||
private val scheduler: Scheduler,
|
private val scheduler: Scheduler,
|
||||||
private val eventsScheduler: Scheduler,
|
private val eventsScheduler: Scheduler,
|
||||||
private val customHeaders: Map<String, String> = emptyMap(),
|
private val customHeaders: Map<String, String> = emptyMap(),
|
||||||
|
private val bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
) : AutoCloseable, WsConnection, Cloneable {
|
) : AutoCloseable, WsConnection, Cloneable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -227,6 +228,11 @@ open class WsConnectionImpl(
|
|||||||
val base64password = Base64.getEncoder().encodeToString(tmp.toByteArray())
|
val base64password = Base64.getEncoder().encodeToString(tmp.toByteArray())
|
||||||
headers.add(HttpHeaderNames.AUTHORIZATION, "Basic $base64password")
|
headers.add(HttpHeaderNames.AUTHORIZATION, "Basic $base64password")
|
||||||
}
|
}
|
||||||
|
if (basicAuth == null) {
|
||||||
|
bearerAuth?.let { auth ->
|
||||||
|
headers.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}")
|
||||||
|
}
|
||||||
|
}
|
||||||
customHeaders.forEach { (key, value) ->
|
customHeaders.forEach { (key, value) ->
|
||||||
headers.add(key, value)
|
headers.add(key, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ class RestHttpReader(
|
|||||||
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
||||||
tlsCAAuth: ByteArray? = null,
|
tlsCAAuth: ByteArray? = null,
|
||||||
customHeaders: Map<String, String> = emptyMap(),
|
customHeaders: Map<String, String> = emptyMap(),
|
||||||
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders) {
|
bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
|
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders, bearerAuth) {
|
||||||
|
|
||||||
private val parser = ResponseRpcParser()
|
private val parser = ResponseRpcParser()
|
||||||
private val requestParser = RestRequestParser
|
private val requestParser = RestRequestParser
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import java.util.function.Function
|
|||||||
/**
|
/**
|
||||||
* JSON RPC client
|
* JSON RPC client
|
||||||
*/
|
*/
|
||||||
class JsonRpcHttpReader(
|
class JsonRpcHttpReader @JvmOverloads constructor(
|
||||||
target: String,
|
target: String,
|
||||||
maxConnections: Int,
|
maxConnections: Int,
|
||||||
queueSize: Int,
|
queueSize: Int,
|
||||||
@@ -45,7 +45,8 @@ class JsonRpcHttpReader(
|
|||||||
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
basicAuth: AuthConfig.ClientBasicAuth? = null,
|
||||||
tlsCAAuth: ByteArray? = null,
|
tlsCAAuth: ByteArray? = null,
|
||||||
customHeaders: Map<String, String> = emptyMap(),
|
customHeaders: Map<String, String> = emptyMap(),
|
||||||
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders) {
|
bearerAuth: AuthConfig.ClientBearerAuth? = null,
|
||||||
|
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders, bearerAuth) {
|
||||||
|
|
||||||
private val parser = ResponseRpcParser()
|
private val parser = ResponseRpcParser()
|
||||||
private val streamParser = JsonRpcStreamParser()
|
private val streamParser = JsonRpcStreamParser()
|
||||||
|
|||||||
@@ -35,6 +35,29 @@ class AuthConfigReaderSpec extends Specification {
|
|||||||
act.password == "258fe4149c199ad8f2811a68f20154fc"
|
act.password == "258fe4149c199ad8f2811a68f20154fc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Read bearer-auth for client"() {
|
||||||
|
setup:
|
||||||
|
def yaml =
|
||||||
|
"bearer-auth:\n" +
|
||||||
|
" token: 9c199ad8f281f20154fc258fe41a6814"
|
||||||
|
when:
|
||||||
|
def act = reader.readClientBearerAuth(reader.readNode(yaml))
|
||||||
|
then:
|
||||||
|
act != null
|
||||||
|
act.token == "9c199ad8f281f20154fc258fe41a6814"
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Read bearer-auth without token as null"() {
|
||||||
|
setup:
|
||||||
|
def yaml =
|
||||||
|
"bearer-auth:\n" +
|
||||||
|
" something: else"
|
||||||
|
when:
|
||||||
|
def act = reader.readClientBearerAuth(reader.readNode(yaml))
|
||||||
|
then:
|
||||||
|
act == null
|
||||||
|
}
|
||||||
|
|
||||||
def "Read tls for client"() {
|
def "Read tls for client"() {
|
||||||
setup:
|
setup:
|
||||||
def yaml =
|
def yaml =
|
||||||
|
|||||||
@@ -75,6 +75,38 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Parse config with bearer auth"() {
|
||||||
|
setup:
|
||||||
|
def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-bearer-auth.yaml")
|
||||||
|
when:
|
||||||
|
def act = reader.readInternal(config)
|
||||||
|
then:
|
||||||
|
act != null
|
||||||
|
act.upstreams.size() == 2
|
||||||
|
with(act.upstreams.get(0)) {
|
||||||
|
id == "local"
|
||||||
|
connection instanceof UpstreamsConfig.RpcConnection
|
||||||
|
with((UpstreamsConfig.RpcConnection) connection) {
|
||||||
|
rpc.basicAuth == null
|
||||||
|
rpc.bearerAuth != null
|
||||||
|
rpc.bearerAuth.token == "9c199ad8f281f20154fc258fe41a6814"
|
||||||
|
ws != null
|
||||||
|
ws.basicAuth == null
|
||||||
|
ws.bearerAuth != null
|
||||||
|
ws.bearerAuth.token == "258fe4149c199ad8f2811a68f20154fc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with(act.upstreams.get(1)) {
|
||||||
|
id == "both-auth"
|
||||||
|
connection instanceof UpstreamsConfig.RpcConnection
|
||||||
|
with((UpstreamsConfig.RpcConnection) connection) {
|
||||||
|
// when both are configured basic-auth wins and bearer-auth is dropped
|
||||||
|
rpc.basicAuth != null
|
||||||
|
rpc.bearerAuth == null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def "Parse websocket-only config"() {
|
def "Parse websocket-only config"() {
|
||||||
setup:
|
setup:
|
||||||
def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-ws-only.yaml")
|
def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-ws-only.yaml")
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.rpcclient
|
package io.emeraldpay.dshackle.upstream.rpcclient
|
||||||
|
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.config.AuthConfig
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
import io.emeraldpay.dshackle.upstream.ChainException
|
import io.emeraldpay.dshackle.upstream.ChainException
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
@@ -69,6 +70,28 @@ class JsonRpcHttpReaderSpec extends Specification {
|
|||||||
new String(act.result) == '"0x98de45"'
|
new String(act.result) == '"0x98de45"'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Make a request with bearer auth"() {
|
||||||
|
setup:
|
||||||
|
def bearerAuth = new AuthConfig.ClientBearerAuth("test-token-123")
|
||||||
|
JsonRpcHttpReader client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:], bearerAuth)
|
||||||
|
def resp = '{' +
|
||||||
|
' "jsonrpc": "2.0",' +
|
||||||
|
' "result": "0x98de45",' +
|
||||||
|
' "error": null,' +
|
||||||
|
' "id": 15' +
|
||||||
|
'}'
|
||||||
|
mockServer.when(
|
||||||
|
HttpRequest.request().withHeader("authorization", "Bearer test-token-123")
|
||||||
|
).respond(
|
||||||
|
HttpResponse.response(resp)
|
||||||
|
)
|
||||||
|
when:
|
||||||
|
def act = client.read(new ChainRequest("test", new ListParams())).block(Duration.ofSeconds(5))
|
||||||
|
then:
|
||||||
|
act.error == null
|
||||||
|
new String(act.result) == '"0x98de45"'
|
||||||
|
}
|
||||||
|
|
||||||
def "Produces RPC Exception on error status code"() {
|
def "Produces RPC Exception on error status code"() {
|
||||||
setup:
|
setup:
|
||||||
def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:])
|
def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:])
|
||||||
|
|||||||
27
src/test/resources/configs/upstreams-bearer-auth.yaml
Normal file
27
src/test/resources/configs/upstreams-bearer-auth.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
version: v1
|
||||||
|
|
||||||
|
upstreams:
|
||||||
|
- id: local
|
||||||
|
chain: ethereum
|
||||||
|
connection:
|
||||||
|
ethereum:
|
||||||
|
rpc:
|
||||||
|
url: "http://localhost:8545"
|
||||||
|
bearer-auth:
|
||||||
|
token: 9c199ad8f281f20154fc258fe41a6814
|
||||||
|
ws:
|
||||||
|
url: "ws://localhost:8546"
|
||||||
|
origin: "http://localhost"
|
||||||
|
bearer-auth:
|
||||||
|
token: 258fe4149c199ad8f2811a68f20154fc
|
||||||
|
- id: both-auth
|
||||||
|
chain: ethereum
|
||||||
|
connection:
|
||||||
|
ethereum:
|
||||||
|
rpc:
|
||||||
|
url: "http://localhost:8545"
|
||||||
|
basic-auth:
|
||||||
|
username: 4fc258fe41a68149c199ad8f281f2015
|
||||||
|
password: 1a68f20154fc258fe4149c199ad8f281
|
||||||
|
bearer-auth:
|
||||||
|
token: 9c199ad8f281f20154fc258fe41a6814
|
||||||
Reference in New Issue
Block a user