mirror of https://github.com/hyperledger/besu
Use the node's configuration to decide if adding a peer with DNS in the enode is allowed (#5584)
* Use the node's configuration to decide if adding a peer with DNS in the enode is allowed Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Update command test mocks Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Update besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Matt Whitehead <matthew1001@hotmail.com> * Address PR comments. Add a reference in the change log. Fix failing integration test. Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Add the same DNS-config checking logic to admin_removePeer, along with a unit test file for it Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Tweak the change log Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Add clearer error messages for the cases where enode DNS is disabled (but a hostname one has been specified in the URL) or where DNS name resolution failed Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Spotless Java fixes Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * Fix copyright for new file Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Matt Whitehead <matthew1001@hotmail.com> * Address PR comments (mainly copyright & constant renaming) Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> * move changelog entry to 23.4.4 Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Matthew Whitehead <matthew1001@gmail.com> Signed-off-by: Matt Whitehead <matthew1001@hotmail.com> Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Matthew Whitehead <matthew1001@gmail.com> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>pull/5627/head
parent
8f9882f768
commit
5bff76f107
@ -0,0 +1,258 @@ |
|||||||
|
/* |
||||||
|
* Copyright Hyperledger Besu contributors |
||||||
|
* |
||||||
|
* 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. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.ArgumentMatchers.any; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.peers.ImmutableEnodeDnsConfiguration; |
||||||
|
|
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.mockito.Mock; |
||||||
|
import org.mockito.junit.MockitoJUnitRunner; |
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.StrictStubs.class) |
||||||
|
public class AdminRemovePeerTest { |
||||||
|
|
||||||
|
@Mock private P2PNetwork p2pNetwork; |
||||||
|
|
||||||
|
private AdminRemovePeer method; |
||||||
|
private AdminRemovePeer methodDNSDisabled; |
||||||
|
private AdminRemovePeer methodDNSUpdateDisabled; |
||||||
|
|
||||||
|
final String validEnode = |
||||||
|
"enode://" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "@127.0.0.1:30303"; |
||||||
|
|
||||||
|
final String validDNSEnode = |
||||||
|
"enode://" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "@node.acme.com:30303"; |
||||||
|
|
||||||
|
final JsonRpcRequestContext validRequest = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validEnode})); |
||||||
|
|
||||||
|
final JsonRpcRequestContext validDNSRequest = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validDNSEnode})); |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setup() { |
||||||
|
method = |
||||||
|
new AdminRemovePeer( |
||||||
|
p2pNetwork, |
||||||
|
Optional.of( |
||||||
|
ImmutableEnodeDnsConfiguration.builder() |
||||||
|
.dnsEnabled(true) |
||||||
|
.updateEnabled(true) |
||||||
|
.build())); |
||||||
|
methodDNSDisabled = |
||||||
|
new AdminRemovePeer( |
||||||
|
p2pNetwork, |
||||||
|
Optional.of( |
||||||
|
ImmutableEnodeDnsConfiguration.builder() |
||||||
|
.dnsEnabled(false) |
||||||
|
.updateEnabled(true) |
||||||
|
.build())); |
||||||
|
methodDNSUpdateDisabled = |
||||||
|
new AdminRemovePeer( |
||||||
|
p2pNetwork, |
||||||
|
Optional.of( |
||||||
|
ImmutableEnodeDnsConfiguration.builder() |
||||||
|
.dnsEnabled(true) |
||||||
|
.updateEnabled(false) |
||||||
|
.build())); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestIsMissingParameter() { |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_removePeer", new String[] {})); |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestHasNullObjectParameter() { |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_removePeer", null)); |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestHasNullArrayParameter() { |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {null})); |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestHasInvalidEnode() { |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {"asdf"})); |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.PARSE_ERROR); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestHasInvalidEnodeLength() { |
||||||
|
String invalidLengthEnode = |
||||||
|
"enode://" |
||||||
|
+ "0000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "00000000000000000000000000000000" |
||||||
|
+ "@127.0.0.1:30303"; |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {invalidLengthEnode})); |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.ENODE_ID_INVALID); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestRemovesValidEnode() { |
||||||
|
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(true); |
||||||
|
|
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcSuccessResponse(validRequest.getRequest().getId(), true); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(validRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestRemovesValidDNSEnode() { |
||||||
|
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(true); |
||||||
|
|
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcSuccessResponse( |
||||||
|
validRequest.getRequest().getId(), |
||||||
|
true); // DNS is mapped to an IP address, so we expect the non-DNS response
|
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(validDNSRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestRemovesDNSEnodeButDNSDisabled() { |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse( |
||||||
|
validDNSRequest.getRequest().getId(), JsonRpcError.DNS_NOT_ENABLED); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = methodDNSDisabled.response(validDNSRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestRemovesDNSEnodeButDNSNotResolved() { |
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse( |
||||||
|
validDNSRequest.getRequest().getId(), JsonRpcError.CANT_RESOLVE_PEER_ENODE_DNS); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = methodDNSUpdateDisabled.response(validDNSRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestRefusesListOfNodes() { |
||||||
|
final JsonRpcRequestContext request = |
||||||
|
new JsonRpcRequestContext( |
||||||
|
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validEnode, validEnode})); |
||||||
|
|
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(request); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestReturnsFalseIfRemoveFails() { |
||||||
|
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(false); |
||||||
|
|
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcSuccessResponse(validRequest.getRequest().getId(), false); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(validRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void requestReturnsErrorWhenP2pDisabled() { |
||||||
|
when(p2pNetwork.removeMaintainedConnectionPeer(any())) |
||||||
|
.thenThrow( |
||||||
|
new P2PDisabledException( |
||||||
|
"P2P networking disabled. Unable to connect to remove peer.")); |
||||||
|
|
||||||
|
final JsonRpcResponse expectedResponse = |
||||||
|
new JsonRpcErrorResponse(validRequest.getRequest().getId(), JsonRpcError.P2P_DISABLED); |
||||||
|
|
||||||
|
final JsonRpcResponse actualResponse = method.response(validRequest); |
||||||
|
|
||||||
|
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue