[NC-2044] Added the authentication service to the websocket service and cli and acceptance tests (#829)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Chris Mckay 6 years ago committed by GitHub
parent f6bb1b2279
commit 5a1389b970
  1. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java
  2. 15
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java
  3. 13
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java
  4. 10
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java
  5. 43
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/WebsocketServiceLoginAcceptanceTest.java
  6. 58
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/authentication/AuthenticationService.java
  7. 20
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/websocket/WebSocketConfiguration.java
  8. 45
      ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/websocket/WebSocketService.java
  9. 151
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/websocket/WebSocketServiceLoginTest.java
  10. 17
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/websocket/WebSocketServiceTest.java
  11. 31
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  12. 2
      pantheon/src/test/resources/everything_config.toml

@ -89,6 +89,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private JsonRequestFactories jsonRequestFactories;
private HttpRequestFactory httpRequestFactory;
private Optional<EthNetworkConfig> ethNetworkConfig = Optional.empty();
private boolean useWsForJsonRpc = false;
public PantheonNode(
final String name,
@ -158,6 +159,18 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
}
}
private Optional<String> wsRpcBaseHttpUrl() {
if (isWebSocketsRpcEnabled()) {
return Optional.of(
"http://"
+ webSocketConfiguration.getHost()
+ ":"
+ portsProperties.getProperty("ws-rpc"));
} else {
return Optional.empty();
}
}
@Override
public Optional<Integer> jsonRpcWebSocketPort() {
if (isWebSocketsRpcEnabled()) {
@ -174,10 +187,19 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private JsonRequestFactories jsonRequestFactories() {
if (jsonRequestFactories == null) {
final Optional<String> baseUrl;
final String port;
if (useWsForJsonRpc) {
baseUrl = wsRpcBaseUrl();
port = "8546";
} else {
baseUrl = jsonRpcBaseUrl();
port = "8545";
}
final Web3jService web3jService =
jsonRpcBaseUrl()
baseUrl
.map(url -> new HttpService(url))
.orElse(new HttpService("http://" + LOCALHOST + ":8545"));
.orElse(new HttpService("http://" + LOCALHOST + ":" + port));
jsonRequestFactories =
new JsonRequestFactories(
@ -193,8 +215,17 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
private HttpRequestFactory httpRequestFactory() {
if (httpRequestFactory == null) {
final Optional<String> baseUrl;
final String port;
if (useWsForJsonRpc) {
baseUrl = wsRpcBaseHttpUrl();
port = "8546";
} else {
baseUrl = jsonRpcBaseUrl();
port = "8545";
}
httpRequestFactory =
new HttpRequestFactory(jsonRpcBaseUrl().orElse("http://" + LOCALHOST + ":8545"));
new HttpRequestFactory(baseUrl.orElse("http://" + LOCALHOST + ":" + port));
}
return httpRequestFactory;
}
@ -216,6 +247,12 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto
if (jsonRequestFactories != null) {
jsonRequestFactories.shutdown();
}
if (httpRequestFactory != null) {
httpRequestFactory = null;
}
useWsForJsonRpc = true;
}
private void checkIfWebSocketEndpointIsAvailable(final String url) {

@ -75,10 +75,10 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
params.add(String.join(",", node.bootnodes().toString()));
if (node.jsonRpcEnabled()) {
params.add("--rpc-enabled");
params.add("--rpc-http-enabled");
params.add("--rpc-listen");
params.add(node.jsonRpcListenAddress().get());
params.add("--rpc-api");
params.add("--rpc-http-api");
params.add(apiList(node.jsonRpcConfiguration().getRpcApis()));
if (node.jsonRpcConfiguration().isAuthenticationEnabled()) {
params.add("--rpc-http-authentication-enabled");
@ -90,11 +90,18 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner {
}
if (node.wsRpcEnabled()) {
params.add("--ws-enabled");
params.add("--rpc-ws-enabled");
params.add("--ws-listen");
params.add(node.wsRpcListenAddress().get());
params.add("--ws-api");
params.add("--rpc-ws-api");
params.add(apiList(node.webSocketConfiguration().getRpcApis()));
if (node.webSocketConfiguration().isAuthenticationEnabled()) {
params.add("--rpc-ws-authentication-enabled");
}
if (node.webSocketConfiguration().getAuthenticationCredentialsFile() != null) {
params.add("--rpc-ws-authentication-credentials-file");
params.add(node.webSocketConfiguration().getAuthenticationCredentialsFile());
}
}
if (node.ethNetworkConfig().isPresent()) {

@ -104,6 +104,19 @@ public class PantheonFactoryConfigurationBuilder {
return this;
}
public PantheonFactoryConfigurationBuilder webSocketAuthenticationEnabled()
throws URISyntaxException {
final String authTomlPath =
Paths.get(ClassLoader.getSystemResource("authentication/auth.toml").toURI())
.toAbsolutePath()
.toString();
this.webSocketConfiguration.setAuthenticationEnabled(true);
this.webSocketConfiguration.setAuthenticationCredentialsFile(authTomlPath);
return this;
}
public PantheonFactoryConfigurationBuilder setPermissioningConfiguration(
final PermissioningConfiguration permissioningConfiguration) {
this.permissioningConfiguration = Optional.of(permissioningConfiguration);

@ -126,6 +126,16 @@ public class PantheonNodeFactory {
.build());
}
public PantheonNode createArchiveNodeWithAuthenticationOverWebSocket(final String name)
throws IOException, URISyntaxException {
return create(
new PantheonFactoryConfigurationBuilder()
.setName(name)
.webSocketEnabled()
.webSocketAuthenticationEnabled()
.build());
}
public PantheonNode createNodeWithP2pDisabled(final String name) throws IOException {
return create(
new PantheonFactoryConfigurationBuilder()

@ -0,0 +1,43 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.tests.acceptance.jsonrpc;
import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode;
import java.io.IOException;
import java.net.URISyntaxException;
import org.junit.Before;
import org.junit.Test;
public class WebsocketServiceLoginAcceptanceTest extends AcceptanceTestBase {
private PantheonNode node;
@Before
public void setUp() throws IOException, URISyntaxException {
node = pantheon.createArchiveNodeWithAuthenticationOverWebSocket("node1");
cluster.start(node);
node.useWebSocketsForJsonRpc();
}
@Test
public void shouldFailLoginWithWrongCredentials() {
node.verify(login.loginFails("user", "badpassword"));
}
@Test
public void shouldSucceedLoginWithCorrectCredentials() {
node.verify(login.loginSucceeds("user", "pegasys"));
}
}

@ -13,12 +13,14 @@
package tech.pegasys.pantheon.ethereum.jsonrpc.authentication;
import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Optional;
import javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
import io.netty.handler.codec.http.HttpResponseStatus;
@ -60,12 +62,16 @@ public class AuthenticationService {
*/
public static Optional<AuthenticationService> create(
final Vertx vertx, final JsonRpcConfiguration config) {
final Optional<JWTAuthOptions> jwtAuthOptions = makeJwtAuthOptions(config);
final Optional<JWTAuthOptions> jwtAuthOptions =
makeJwtAuthOptions(
config.isAuthenticationEnabled(), config.getAuthenticationCredentialsFile());
if (!jwtAuthOptions.isPresent()) {
return Optional.empty();
}
final Optional<AuthProvider> credentialAuthProvider = makeCredentialAuthProvider(vertx, config);
final Optional<AuthProvider> credentialAuthProvider =
makeCredentialAuthProvider(
vertx, config.isAuthenticationEnabled(), config.getAuthenticationCredentialsFile());
if (!credentialAuthProvider.isPresent()) {
return Optional.empty();
}
@ -77,8 +83,42 @@ public class AuthenticationService {
credentialAuthProvider.get()));
}
private static Optional<JWTAuthOptions> makeJwtAuthOptions(final JsonRpcConfiguration config) {
if (config.isAuthenticationEnabled() && config.getAuthenticationCredentialsFile() != null) {
/**
* Creates a ready for use set of authentication providers if authentication is configured to be
* on
*
* @param vertx The vertx instance that will be providing requests that this set of authentication
* providers will be handling
* @param config The {{@link JsonRpcConfiguration}} that describes this rpc setup
* @return Optionally an authentication service. If empty then authentication isn't to be enabled
* on this service
*/
public static Optional<AuthenticationService> create(
final Vertx vertx, final WebSocketConfiguration config) {
final Optional<JWTAuthOptions> jwtAuthOptions =
makeJwtAuthOptions(
config.isAuthenticationEnabled(), config.getAuthenticationCredentialsFile());
if (!jwtAuthOptions.isPresent()) {
return Optional.empty();
}
final Optional<AuthProvider> credentialAuthProvider =
makeCredentialAuthProvider(
vertx, config.isAuthenticationEnabled(), config.getAuthenticationCredentialsFile());
if (!credentialAuthProvider.isPresent()) {
return Optional.empty();
}
return Optional.of(
new AuthenticationService(
jwtAuthOptions.map(o -> JWTAuth.create(vertx, o)).get(),
jwtAuthOptions.get(),
credentialAuthProvider.get()));
}
private static Optional<JWTAuthOptions> makeJwtAuthOptions(
final boolean authenticationEnabled, @Nullable final String authenticationCredentialsFile) {
if (authenticationEnabled && authenticationCredentialsFile != null) {
final KeyPairGenerator keyGenerator;
try {
keyGenerator = KeyPairGenerator.getInstance("RSA");
@ -107,12 +147,12 @@ public class AuthenticationService {
}
private static Optional<AuthProvider> makeCredentialAuthProvider(
final Vertx vertx, final JsonRpcConfiguration config) {
if (config.isAuthenticationEnabled() && config.getAuthenticationCredentialsFile() != null) {
final Vertx vertx,
final boolean authenticationEnabled,
@Nullable final String authenticationCredentialsFile) {
if (authenticationEnabled && authenticationCredentialsFile != null) {
return Optional.of(
new TomlAuthOptions()
.setTomlPath(config.getAuthenticationCredentialsFile())
.createProvider(vertx));
new TomlAuthOptions().setTomlPath(authenticationCredentialsFile).createProvider(vertx));
} else {
return Optional.empty();
}

@ -35,6 +35,8 @@ public class WebSocketConfiguration {
private String host;
private Collection<RpcApi> rpcApis;
private long refreshDelay;
private boolean authenticationEnabled = false;
private String authenticationCredentialsFile;
public static WebSocketConfiguration createDefault() {
final WebSocketConfiguration config = new WebSocketConfiguration();
@ -92,6 +94,8 @@ public class WebSocketConfiguration {
.add("port", port)
.add("host", host)
.add("rpcApis", rpcApis)
.add("authenticationEnabled", authenticationEnabled)
.add("authenticationCredentialsFile", authenticationCredentialsFile)
.toString();
}
@ -122,4 +126,20 @@ public class WebSocketConfiguration {
public long getRefreshDelay() {
return refreshDelay;
}
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
}
public void setAuthenticationEnabled(final boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
}
public void setAuthenticationCredentialsFile(final String authenticationCredentialsFile) {
this.authenticationCredentialsFile = authenticationCredentialsFile;
}
public String getAuthenticationCredentialsFile() {
return authenticationCredentialsFile;
}
}

@ -12,11 +12,14 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.websocket;
import tech.pegasys.pantheon.ethereum.jsonrpc.authentication.AuthenticationService;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.subscription.SubscriptionManager;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import com.google.common.annotations.VisibleForTesting;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
@ -25,6 +28,8 @@ import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -33,6 +38,7 @@ public class WebSocketService {
private static final Logger LOG = LogManager.getLogger();
private static final InetSocketAddress EMPTY_SOCKET_ADDRESS = new InetSocketAddress("0.0.0.0", 0);
private static final String APPLICATION_JSON = "application/json";
private final Vertx vertx;
private final WebSocketConfiguration configuration;
@ -40,13 +46,28 @@ public class WebSocketService {
private HttpServer httpServer;
@VisibleForTesting public final Optional<AuthenticationService> authenticationService;
public WebSocketService(
final Vertx vertx,
final WebSocketConfiguration configuration,
final WebSocketRequestHandler websocketRequestHandler) {
this(
vertx,
configuration,
websocketRequestHandler,
AuthenticationService.create(vertx, configuration));
}
private WebSocketService(
final Vertx vertx,
final WebSocketConfiguration configuration,
final WebSocketRequestHandler websocketRequestHandler,
final Optional<AuthenticationService> authenticationService) {
this.vertx = vertx;
this.configuration = configuration;
this.websocketRequestHandler = websocketRequestHandler;
this.authenticationService = authenticationService;
}
public CompletableFuture<?> start() {
@ -106,8 +127,28 @@ public class WebSocketService {
}
private Handler<HttpServerRequest> httpHandler() {
return http ->
http.response().setStatusCode(400).end("Websocket endpoint can't handle HTTP requests");
final Router router = Router.router(vertx);
if (authenticationService.isPresent()) {
router.route("/login").handler(BodyHandler.create());
router
.post("/login")
.produces(APPLICATION_JSON)
.handler(authenticationService.get()::handleLogin);
} else {
router
.post("/login")
.produces(APPLICATION_JSON)
.handler(AuthenticationService::handleDisabledLogin);
}
router
.route()
.handler(
http ->
http.response()
.setStatusCode(400)
.end("Websocket endpoint can't handle HTTP requests"));
return router;
}
private Handler<AsyncResult<HttpServer>> startHandler(final CompletableFuture<?> resultFuture) {

@ -0,0 +1,151 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.websocket;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.methods.WebSocketMethodsFactory;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.subscription.SubscriptionManager;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VertxUnitRunner.class)
public class WebSocketServiceLoginTest {
private static final int VERTX_AWAIT_TIMEOUT_MILLIS = 10000;
private Vertx vertx;
private WebSocketConfiguration websocketConfiguration;
private WebSocketRequestHandler webSocketRequestHandlerSpy;
private WebSocketService websocketService;
private HttpClient httpClient;
@Before
public void before() throws URISyntaxException {
vertx = Vertx.vertx();
final String authTomlPath =
Paths.get(ClassLoader.getSystemResource("JsonRpcHttpService/auth.toml").toURI())
.toAbsolutePath()
.toString();
websocketConfiguration = WebSocketConfiguration.createDefault();
websocketConfiguration.setPort(0);
websocketConfiguration.setAuthenticationEnabled(true);
websocketConfiguration.setAuthenticationCredentialsFile(authTomlPath);
final Map<String, JsonRpcMethod> websocketMethods =
new WebSocketMethodsFactory(new SubscriptionManager(), new HashMap<>()).methods();
webSocketRequestHandlerSpy = spy(new WebSocketRequestHandler(vertx, websocketMethods));
websocketService =
new WebSocketService(vertx, websocketConfiguration, webSocketRequestHandlerSpy);
websocketService.start().join();
websocketConfiguration.setPort(websocketService.socketAddress().getPort());
final HttpClientOptions httpClientOptions =
new HttpClientOptions()
.setDefaultHost(websocketConfiguration.getHost())
.setDefaultPort(websocketConfiguration.getPort());
httpClient = vertx.createHttpClient(httpClientOptions);
}
@After
public void after() {
reset(webSocketRequestHandlerSpy);
websocketService.stop();
}
@Test
public void loginWithBadCredentials() {
final HttpClientRequest request =
httpClient.post(
websocketConfiguration.getPort(),
websocketConfiguration.getHost(),
"/login",
response -> {
assertThat(response.statusCode()).isEqualTo(401);
assertThat(response.statusMessage()).isEqualTo("Unauthorized");
});
request.putHeader("Content-Type", "application/json; charset=utf-8");
request.end("{\"username\":\"user\",\"password\":\"pass\"}");
}
@Test
public void loginWithGoodCredentials() {
final HttpClientRequest request =
httpClient.post(
websocketConfiguration.getPort(),
websocketConfiguration.getHost(),
"/login",
response -> {
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.statusMessage()).isEqualTo("OK");
assertThat(response.getHeader("Content-Type")).isNotNull();
assertThat(response.getHeader("Content-Type")).isEqualTo("application/json");
response.bodyHandler(
buffer -> {
final String body = buffer.toString();
assertThat(body).isNotBlank();
final JsonObject respBody = new JsonObject(body);
final String token = respBody.getString("token");
assertThat(token).isNotNull();
websocketService
.authenticationService
.get()
.getJwtAuthProvider()
.authenticate(
new JsonObject().put("jwt", token),
(r) -> {
assertThat(r.succeeded()).isTrue();
final User user = r.result();
user.isAuthorized(
"noauths",
(authed) -> {
assertThat(authed.succeeded()).isTrue();
assertThat(authed.result()).isFalse();
});
user.isAuthorized(
"fakePermission",
(authed) -> {
assertThat(authed.succeeded()).isTrue();
assertThat(authed.result()).isTrue();
});
});
});
});
request.putHeader("Content-Type", "application/json; charset=utf-8");
request.end("{\"username\":\"user\",\"password\":\"pegasys\"}");
}
}

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.jsonrpc.websocket;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@ -27,6 +28,7 @@ import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.WebSocketBase;
import io.vertx.ext.unit.Async;
@ -158,4 +160,19 @@ public class WebSocketServiceTest {
async.awaitSuccess(VERTX_AWAIT_TIMEOUT_MILLIS);
}
@Test
public void handleLoginRequestWithAuthDisabled() {
final HttpClientRequest request =
httpClient.post(
websocketConfiguration.getPort(),
websocketConfiguration.getHost(),
"/login",
response -> {
assertThat(response.statusCode()).isEqualTo(400);
assertThat(response.statusMessage()).isEqualTo("Authentication not enabled");
});
request.putHeader("Content-Type", "application/json; charset=utf-8");
request.end("{\"username\":\"user\",\"password\":\"pass\"}");
}
}

@ -277,7 +277,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option(
names = {"--rpc-http-authentication-credentials-file"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Storage file for rpc http authentication credentials (default: ${DEFAULT-VALUE})",
arity = "1",
@ -337,6 +337,22 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return refreshDelay;
}
@Option(
names = {"--rpc-ws-authentication-enabled"},
description =
"Set if the websocket JSON-RPC service should require authentication (default: ${DEFAULT-VALUE})",
hidden = true)
private final Boolean isRpcWsAuthenticationEnabled = false;
@Option(
names = {"--rpc-ws-authentication-credentials-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Storage file for rpc websocket authentication credentials (default: ${DEFAULT-VALUE})",
arity = "1",
hidden = true)
private String rpcWsAuthenticationCredentialsFile = null;
@Option(
names = {"--metrics-enabled"},
description = "Set if the metrics exporter should be started (default: ${DEFAULT-VALUE})")
@ -677,7 +693,16 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
"--rpc-ws-apis",
"--rpc-ws-refresh-delay",
"--rpc-ws-host",
"--rpc-ws-port"));
"--rpc-ws-port",
"--rpc-ws-authentication-enabled",
"--rpc-ws-authentication-credentials-file"));
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-authentication-enabled",
!isRpcWsAuthenticationEnabled,
Collections.singletonList("--rpc-ws-authentication-credentials-file"));
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(isRpcWsEnabled);
@ -685,6 +710,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
webSocketConfiguration.setPort(rpcWsPort);
webSocketConfiguration.setRpcApis(rpcWsApis);
webSocketConfiguration.setRefreshDelay(rpcWsRefreshDelay);
webSocketConfiguration.setAuthenticationEnabled(isRpcWsAuthenticationEnabled);
webSocketConfiguration.setAuthenticationCredentialsFile(rpcWsAuthenticationCredentialsFile);
return webSocketConfiguration;
}

@ -51,6 +51,8 @@ rpc-ws-apis=["DEBUG","ETH"]
rpc-ws-host="9.10.11.12"
rpc-ws-port=9101
rpc-ws-refresh-delay=500
rpc-ws-authentication-enabled=false
rpc-ws-authentication-credentials-file="none"
# Prometheus Metrics Endpoint
metrics-enabled=false

Loading…
Cancel
Save