mirror of https://github.com/hyperledger/besu
[PAN-2614] Expand permissioning interface (#1471)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
c23482c60b
commit
22ec27796d
@ -0,0 +1,54 @@ |
||||
/* |
||||
* 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.p2p.discovery.internal; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions.Action; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PermissionsUpdateCallback; |
||||
|
||||
class PeerDiscoveryPermissions { |
||||
private final Peer localNode; |
||||
private final PeerPermissions peerPermissions; |
||||
|
||||
PeerDiscoveryPermissions(final Peer localNode, final PeerPermissions peerPermissions) { |
||||
this.localNode = localNode; |
||||
this.peerPermissions = peerPermissions; |
||||
} |
||||
|
||||
public void subscribeUpdate(final PermissionsUpdateCallback callback) { |
||||
peerPermissions.subscribeUpdate(callback); |
||||
} |
||||
|
||||
public boolean isAllowedInPeerTable(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.DISCOVERY_ALLOW_IN_PEER_TABLE); |
||||
} |
||||
|
||||
public boolean allowOutboundBonding(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.DISCOVERY_ALLOW_OUTBOUND_BONDING); |
||||
} |
||||
|
||||
public boolean allowInboundBonding(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.DISCOVERY_ACCEPT_INBOUND_BONDING); |
||||
} |
||||
|
||||
public boolean allowOutboundNeighborsRequest(final Peer peer) { |
||||
return peerPermissions.isPermitted( |
||||
localNode, peer, Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST); |
||||
} |
||||
|
||||
public boolean allowInboundNeighborsRequest(final Peer peer) { |
||||
return peerPermissions.isPermitted( |
||||
localNode, peer, Action.DISCOVERY_SERVE_INBOUND_NEIGHBORS_REQUEST); |
||||
} |
||||
} |
@ -0,0 +1,130 @@ |
||||
/* |
||||
* 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.p2p.network; |
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; |
||||
import tech.pegasys.pantheon.util.enode.EnodeURL; |
||||
|
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
class NodePermissioningAdapter extends PeerPermissions { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private final NodePermissioningController nodePermissioningController; |
||||
private final List<EnodeURL> bootnodes; |
||||
private final Blockchain blockchain; |
||||
private final long blockchainListenId; |
||||
private final long nodePermissioningListenId; |
||||
|
||||
public NodePermissioningAdapter( |
||||
final NodePermissioningController nodePermissioningController, |
||||
final List<EnodeURL> bootnodes, |
||||
final Blockchain blockchain) { |
||||
checkNotNull(nodePermissioningController); |
||||
checkNotNull(bootnodes); |
||||
checkNotNull(blockchain); |
||||
|
||||
this.nodePermissioningController = nodePermissioningController; |
||||
this.bootnodes = bootnodes; |
||||
this.blockchain = blockchain; |
||||
|
||||
// TODO: These events should be more targeted
|
||||
blockchainListenId = |
||||
blockchain.observeBlockAdded((evt, chain) -> dispatchUpdate(true, Optional.empty())); |
||||
nodePermissioningListenId = |
||||
this.nodePermissioningController.subscribeToUpdates( |
||||
() -> dispatchUpdate(true, Optional.empty())); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isPermitted(final Peer localNode, final Peer remotePeer, final Action action) { |
||||
switch (action) { |
||||
case DISCOVERY_ALLOW_IN_PEER_TABLE: |
||||
return outboundIsPermitted(localNode, remotePeer); |
||||
case DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST: |
||||
return allowOutboundNeighborsRequests(localNode, remotePeer); |
||||
case DISCOVERY_ALLOW_OUTBOUND_BONDING: |
||||
return allowOutboundBonding(localNode, remotePeer); |
||||
case DISCOVERY_ACCEPT_INBOUND_BONDING: |
||||
case DISCOVERY_SERVE_INBOUND_NEIGHBORS_REQUEST: |
||||
case RLPX_ALLOW_NEW_INBOUND_CONNECTION: |
||||
return inboundIsPermitted(localNode, remotePeer); |
||||
case RLPX_ALLOW_NEW_OUTBOUND_CONNECTION: |
||||
return outboundIsPermitted(localNode, remotePeer); |
||||
case RLPX_ALLOW_ONGOING_CONNECTION: |
||||
// TODO: This should probably check outbound || inbound, or the check should be more
|
||||
// granular
|
||||
return outboundIsPermitted(localNode, remotePeer); |
||||
default: |
||||
// Return false for unknown / unhandled permissions
|
||||
LOG.error( |
||||
"Permissions denied for unknown action {}", |
||||
action.name(), |
||||
new IllegalStateException("Unhandled permissions action " + action.name())); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private boolean allowOutboundBonding(final Peer localNode, final Peer remotePeer) { |
||||
boolean outboundMessagingAllowed = outboundIsPermitted(localNode, remotePeer); |
||||
if (!nodePermissioningController.getSyncStatusNodePermissioningProvider().isPresent()) { |
||||
return outboundMessagingAllowed; |
||||
} |
||||
|
||||
// We're using smart-contract based permissioning
|
||||
// If we're out of sync, only allow bonding to our bootnodes
|
||||
final SyncStatusNodePermissioningProvider syncStatus = |
||||
nodePermissioningController.getSyncStatusNodePermissioningProvider().get(); |
||||
return outboundMessagingAllowed |
||||
&& (syncStatus.hasReachedSync() || bootnodes.contains(remotePeer.getEnodeURL())); |
||||
} |
||||
|
||||
private boolean allowOutboundNeighborsRequests(final Peer localNode, final Peer remotePeer) { |
||||
boolean outboundMessagingAllowed = outboundIsPermitted(localNode, remotePeer); |
||||
if (!nodePermissioningController.getSyncStatusNodePermissioningProvider().isPresent()) { |
||||
return outboundMessagingAllowed; |
||||
} |
||||
|
||||
// We're using smart-contract based permissioning
|
||||
// Only allow neighbors requests if we're in sync
|
||||
final SyncStatusNodePermissioningProvider syncStatus = |
||||
nodePermissioningController.getSyncStatusNodePermissioningProvider().get(); |
||||
return outboundMessagingAllowed && syncStatus.hasReachedSync(); |
||||
} |
||||
|
||||
private boolean outboundIsPermitted(final Peer localNode, final Peer remotePeer) { |
||||
return nodePermissioningController.isPermitted( |
||||
localNode.getEnodeURL(), remotePeer.getEnodeURL()); |
||||
} |
||||
|
||||
private boolean inboundIsPermitted(final Peer localNode, final Peer remotePeer) { |
||||
return nodePermissioningController.isPermitted( |
||||
remotePeer.getEnodeURL(), localNode.getEnodeURL()); |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
blockchain.removeObserver(blockchainListenId); |
||||
nodePermissioningController.unsubscribeFromUpdates(nodePermissioningListenId); |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
/* |
||||
* 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.p2p.rlpx; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions.Action; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PermissionsUpdateCallback; |
||||
|
||||
public class PeerRlpxPermissions { |
||||
private final Peer localNode; |
||||
private final PeerPermissions peerPermissions; |
||||
|
||||
public PeerRlpxPermissions(final Peer localNode, final PeerPermissions peerPermissions) { |
||||
this.localNode = localNode; |
||||
this.peerPermissions = peerPermissions; |
||||
} |
||||
|
||||
public boolean allowNewOutboundConnectionTo(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.RLPX_ALLOW_NEW_OUTBOUND_CONNECTION); |
||||
} |
||||
|
||||
public boolean allowNewInboundConnectionFrom(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.RLPX_ALLOW_NEW_INBOUND_CONNECTION); |
||||
} |
||||
|
||||
public boolean allowOngoingConnection(final Peer peer) { |
||||
return peerPermissions.isPermitted(localNode, peer, Action.RLPX_ALLOW_ONGOING_CONNECTION); |
||||
} |
||||
|
||||
public void subscribeUpdate(final PermissionsUpdateCallback callback) { |
||||
peerPermissions.subscribeUpdate(callback); |
||||
} |
||||
} |
@ -1,105 +0,0 @@ |
||||
/* |
||||
* Copyright 2018 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.p2p; |
||||
|
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.doAnswer; |
||||
import static org.mockito.Mockito.spy; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; |
||||
import tech.pegasys.pantheon.util.enode.EnodeURL; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import org.mockito.ArgumentCaptor; |
||||
|
||||
public class NodePermissioningControllerTestHelper { |
||||
|
||||
private final EnodeURL localNode; |
||||
private final Collection<EnodeURL> permittedNodes = new ArrayList<>(); |
||||
private final Collection<EnodeURL> notPermittedNodes = new ArrayList<>(); |
||||
private boolean allowAll = false; |
||||
private boolean denyAll = false; |
||||
|
||||
public NodePermissioningControllerTestHelper(final Peer localPeer) { |
||||
this.localNode = localPeer.getEnodeURL(); |
||||
} |
||||
|
||||
public NodePermissioningControllerTestHelper withPermittedPeers(final Peer... peers) { |
||||
this.permittedNodes.addAll( |
||||
Arrays.stream(peers).map(Peer::getEnodeURL).collect(Collectors.toList())); |
||||
return this; |
||||
} |
||||
|
||||
public NodePermissioningControllerTestHelper withForbiddenPeers(final Peer... peers) { |
||||
this.notPermittedNodes.addAll( |
||||
Arrays.stream(peers).map(Peer::getEnodeURL).collect(Collectors.toList())); |
||||
return this; |
||||
} |
||||
|
||||
public NodePermissioningControllerTestHelper allowAll() { |
||||
this.allowAll = true; |
||||
return this; |
||||
} |
||||
|
||||
public NodePermissioningControllerTestHelper denyAll() { |
||||
this.denyAll = true; |
||||
return this; |
||||
} |
||||
|
||||
public NodePermissioningController build() { |
||||
final NodePermissioningController nodePermissioningController = |
||||
spy(new NodePermissioningController(Optional.empty(), new ArrayList<>())); |
||||
|
||||
if (allowAll && denyAll) { |
||||
throw new IllegalArgumentException( |
||||
"Can't allow all nodes and deny all nodes in the same NodePermissioningController"); |
||||
} else if (allowAll) { |
||||
when(nodePermissioningController.isPermitted(any(), any())).thenReturn(true); |
||||
} else if (denyAll) { |
||||
when(nodePermissioningController.isPermitted(any(), any())).thenReturn(false); |
||||
} else { |
||||
permittedNodes.forEach( |
||||
node -> { |
||||
when(nodePermissioningController.isPermitted(eq(localNode), eq(node))).thenReturn(true); |
||||
when(nodePermissioningController.isPermitted(eq(node), eq(localNode))).thenReturn(true); |
||||
}); |
||||
|
||||
notPermittedNodes.forEach( |
||||
node -> { |
||||
when(nodePermissioningController.isPermitted(eq(localNode), eq(node))) |
||||
.thenReturn(false); |
||||
when(nodePermissioningController.isPermitted(eq(node), eq(localNode))) |
||||
.thenReturn(false); |
||||
}); |
||||
} |
||||
|
||||
ArgumentCaptor<Runnable> callback = ArgumentCaptor.forClass(Runnable.class); |
||||
doAnswer( |
||||
(i) -> { |
||||
callback.getValue().run(); |
||||
return null; |
||||
}) |
||||
.when(nodePermissioningController) |
||||
.startPeerDiscoveryCallback(callback.capture()); |
||||
|
||||
return nodePermissioningController; |
||||
} |
||||
} |
@ -0,0 +1,370 @@ |
||||
/* |
||||
* 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.p2p.network; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.permissions.PeerPermissions.Action; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; |
||||
import tech.pegasys.pantheon.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; |
||||
import tech.pegasys.pantheon.util.enode.EnodeURL; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
public class NodePermissioningAdapterTest { |
||||
|
||||
private final Peer localNode = createPeer(); |
||||
private final Peer remoteNode = createPeer(); |
||||
private final NodePermissioningController nodePermissioningController = |
||||
mock(NodePermissioningController.class); |
||||
private final Blockchain blockchain = mock(Blockchain.class); |
||||
private final List<EnodeURL> bootNodes = new ArrayList<>(); |
||||
private final NodePermissioningAdapter adapter = |
||||
new NodePermissioningAdapter(nodePermissioningController, bootNodes, blockchain); |
||||
|
||||
@Test |
||||
public void allowInPeerTable() { |
||||
final Action action = Action.DISCOVERY_ALLOW_IN_PEER_TABLE; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundBonding_inSyncRemoteIsBootnode() { |
||||
mockSyncStatusNodePermissioning(true, true); |
||||
bootNodes.add(remoteNode.getEnodeURL()); |
||||
|
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundBonding_inSyncRemoteIsNotABootnode() { |
||||
mockSyncStatusNodePermissioning(true, true); |
||||
|
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundBonding_outOfSyncRemoteIsNotABootnode() { |
||||
mockSyncStatusNodePermissioning(true, false); |
||||
|
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundBonding_outOfSyncRemoteIsABootnode() { |
||||
mockSyncStatusNodePermissioning(true, false); |
||||
bootNodes.add(remoteNode.getEnodeURL()); |
||||
|
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundBonding_noSyncPermissioning() { |
||||
mockSyncStatusNodePermissioning(false, false); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowInboundBonding() { |
||||
final Action action = Action.DISCOVERY_ACCEPT_INBOUND_BONDING; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundNeighborsRequest_inSyncRemoteIsBootnode() { |
||||
mockSyncStatusNodePermissioning(true, true); |
||||
bootNodes.add(remoteNode.getEnodeURL()); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundNeighborsRequest_inSyncRemoteIsNotABootnode() { |
||||
mockSyncStatusNodePermissioning(true, true); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundNeighborsRequest_outOfSyncRemoteIsABootnode() { |
||||
mockSyncStatusNodePermissioning(true, false); |
||||
bootNodes.add(remoteNode.getEnodeURL()); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundNeighborsRequest_outOfSyncRemoteIsNotABootnode() { |
||||
mockSyncStatusNodePermissioning(true, false); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOutboundNeighborsRequest_noSyncPermissioning() { |
||||
mockSyncStatusNodePermissioning(false, false); |
||||
final Action action = Action.DISCOVERY_ALLOW_OUTBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowInboundNeighborsRequest() { |
||||
final Action action = Action.DISCOVERY_SERVE_INBOUND_NEIGHBORS_REQUEST; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowLocallyInitiatedConnection() { |
||||
final Action action = Action.RLPX_ALLOW_NEW_OUTBOUND_CONNECTION; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowRemotelyInitiatedConnection() { |
||||
final Action action = Action.RLPX_ALLOW_NEW_INBOUND_CONNECTION; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void allowOngoingConnection() { |
||||
final Action action = Action.RLPX_ALLOW_ONGOING_CONNECTION; |
||||
|
||||
mockControllerPermissions(true, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
|
||||
mockControllerPermissions(true, true); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isTrue(); |
||||
|
||||
mockControllerPermissions(false, false); |
||||
assertThat(adapter.isPermitted(localNode, remoteNode, action)).isFalse(); |
||||
} |
||||
|
||||
private void mockSyncStatusNodePermissioning(final boolean isPresent, final boolean isInSync) { |
||||
if (!isPresent) { |
||||
when(nodePermissioningController.getSyncStatusNodePermissioningProvider()) |
||||
.thenReturn(Optional.empty()); |
||||
return; |
||||
} |
||||
|
||||
final SyncStatusNodePermissioningProvider syncStatus = |
||||
mock(SyncStatusNodePermissioningProvider.class); |
||||
when(syncStatus.hasReachedSync()).thenReturn(isInSync); |
||||
when(nodePermissioningController.getSyncStatusNodePermissioningProvider()) |
||||
.thenReturn(Optional.of(syncStatus)); |
||||
} |
||||
|
||||
private void mockControllerPermissions( |
||||
final boolean allowLocalToRemote, final boolean allowRemoteToLocal) { |
||||
when(nodePermissioningController.isPermitted( |
||||
eq(localNode.getEnodeURL()), eq(remoteNode.getEnodeURL()))) |
||||
.thenReturn(allowLocalToRemote); |
||||
when(nodePermissioningController.isPermitted( |
||||
eq(remoteNode.getEnodeURL()), eq(localNode.getEnodeURL()))) |
||||
.thenReturn(allowRemoteToLocal); |
||||
} |
||||
|
||||
private static Peer createPeer() { |
||||
return DefaultPeer.fromEnodeURL(createEnode()); |
||||
} |
||||
|
||||
private static EnodeURL createEnode() { |
||||
return EnodeURL.builder() |
||||
.ipAddress("127.0.0.1") |
||||
.useDefaultPorts() |
||||
.nodeId(Peer.randomId()) |
||||
.build(); |
||||
} |
||||
} |
Loading…
Reference in new issue