mirror of https://github.com/hyperledger/besu
commit
5d2361ecc2
@ -0,0 +1,206 @@ |
|||||||
|
/* |
||||||
|
* 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.cli.options.stable; |
||||||
|
|
||||||
|
import org.hyperledger.besu.cli.DefaultCommandValues; |
||||||
|
import org.hyperledger.besu.datatypes.Address; |
||||||
|
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfigurationBuilder; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration; |
||||||
|
|
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import picocli.CommandLine; |
||||||
|
|
||||||
|
/** Handles configuration options for permissions in Besu. */ |
||||||
|
public class PermissionsOptions { |
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-nodes-config-file-enabled"}, |
||||||
|
description = "Enable node level permissions (default: ${DEFAULT-VALUE})") |
||||||
|
private final Boolean permissionsNodesEnabled = false; |
||||||
|
|
||||||
|
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-nodes-config-file"}, |
||||||
|
description = |
||||||
|
"Node permissioning config TOML file (default: a file named \"permissions_config.toml\" in the Besu data folder)") |
||||||
|
private String nodePermissionsConfigFile = null; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-accounts-config-file-enabled"}, |
||||||
|
description = "Enable account level permissions (default: ${DEFAULT-VALUE})") |
||||||
|
private final Boolean permissionsAccountsEnabled = false; |
||||||
|
|
||||||
|
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-accounts-config-file"}, |
||||||
|
description = |
||||||
|
"Account permissioning config TOML file (default: a file named \"permissions_config.toml\" in the Besu data folder)") |
||||||
|
private String accountPermissionsConfigFile = null; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-nodes-contract-address"}, |
||||||
|
description = "Address of the node permissioning smart contract", |
||||||
|
arity = "1") |
||||||
|
private final Address permissionsNodesContractAddress = null; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-nodes-contract-version"}, |
||||||
|
description = "Version of the EEA Node Permissioning interface (default: ${DEFAULT-VALUE})") |
||||||
|
private final Integer permissionsNodesContractVersion = 1; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-nodes-contract-enabled"}, |
||||||
|
description = "Enable node level permissions via smart contract (default: ${DEFAULT-VALUE})") |
||||||
|
private final Boolean permissionsNodesContractEnabled = false; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-accounts-contract-address"}, |
||||||
|
description = "Address of the account permissioning smart contract", |
||||||
|
arity = "1") |
||||||
|
private final Address permissionsAccountsContractAddress = null; |
||||||
|
|
||||||
|
@CommandLine.Option( |
||||||
|
names = {"--permissions-accounts-contract-enabled"}, |
||||||
|
description = |
||||||
|
"Enable account level permissions via smart contract (default: ${DEFAULT-VALUE})") |
||||||
|
private final Boolean permissionsAccountsContractEnabled = false; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a PermissioningConfiguration based on the provided options. |
||||||
|
* |
||||||
|
* @param jsonRpcHttpOptions The JSON-RPC HTTP options |
||||||
|
* @param rpcWebsocketOptions The RPC websocket options |
||||||
|
* @param enodeDnsConfiguration The enode DNS configuration |
||||||
|
* @param dataPath The data path |
||||||
|
* @param logger The logger |
||||||
|
* @param commandLine The command line |
||||||
|
* @return An Optional PermissioningConfiguration instance |
||||||
|
* @throws Exception If an error occurs while creating the configuration |
||||||
|
*/ |
||||||
|
public Optional<PermissioningConfiguration> permissioningConfiguration( |
||||||
|
final JsonRpcHttpOptions jsonRpcHttpOptions, |
||||||
|
final RpcWebsocketOptions rpcWebsocketOptions, |
||||||
|
final EnodeDnsConfiguration enodeDnsConfiguration, |
||||||
|
final Path dataPath, |
||||||
|
final Logger logger, |
||||||
|
final CommandLine commandLine) |
||||||
|
throws Exception { |
||||||
|
if (!(localPermissionsEnabled() || contractPermissionsEnabled())) { |
||||||
|
if (jsonRpcHttpOptions.getRpcHttpApis().contains(RpcApis.PERM.name()) |
||||||
|
|| rpcWebsocketOptions.getRpcWsApis().contains(RpcApis.PERM.name())) { |
||||||
|
logger.warn( |
||||||
|
"Permissions are disabled. Cannot enable PERM APIs when not using Permissions."); |
||||||
|
} |
||||||
|
return Optional.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
final Optional<LocalPermissioningConfiguration> localPermissioningConfigurationOptional; |
||||||
|
if (localPermissionsEnabled()) { |
||||||
|
final Optional<String> nodePermissioningConfigFile = |
||||||
|
Optional.ofNullable(nodePermissionsConfigFile); |
||||||
|
final Optional<String> accountPermissioningConfigFile = |
||||||
|
Optional.ofNullable(accountPermissionsConfigFile); |
||||||
|
|
||||||
|
final LocalPermissioningConfiguration localPermissioningConfiguration = |
||||||
|
PermissioningConfigurationBuilder.permissioningConfiguration( |
||||||
|
permissionsNodesEnabled, |
||||||
|
enodeDnsConfiguration, |
||||||
|
nodePermissioningConfigFile.orElse(getDefaultPermissioningFilePath(dataPath)), |
||||||
|
permissionsAccountsEnabled, |
||||||
|
accountPermissioningConfigFile.orElse(getDefaultPermissioningFilePath(dataPath))); |
||||||
|
|
||||||
|
localPermissioningConfigurationOptional = Optional.of(localPermissioningConfiguration); |
||||||
|
} else { |
||||||
|
if (nodePermissionsConfigFile != null && !permissionsNodesEnabled) { |
||||||
|
logger.warn( |
||||||
|
"Node permissioning config file set {} but no permissions enabled", |
||||||
|
nodePermissionsConfigFile); |
||||||
|
} |
||||||
|
|
||||||
|
if (accountPermissionsConfigFile != null && !permissionsAccountsEnabled) { |
||||||
|
logger.warn( |
||||||
|
"Account permissioning config file set {} but no permissions enabled", |
||||||
|
accountPermissionsConfigFile); |
||||||
|
} |
||||||
|
localPermissioningConfigurationOptional = Optional.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration = |
||||||
|
SmartContractPermissioningConfiguration.createDefault(); |
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(permissionsNodesContractEnabled)) { |
||||||
|
if (permissionsNodesContractAddress == null) { |
||||||
|
throw new CommandLine.ParameterException( |
||||||
|
commandLine, |
||||||
|
"No node permissioning contract address specified. Cannot enable smart contract based node permissioning."); |
||||||
|
} else { |
||||||
|
smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled( |
||||||
|
permissionsNodesContractEnabled); |
||||||
|
smartContractPermissioningConfiguration.setNodeSmartContractAddress( |
||||||
|
permissionsNodesContractAddress); |
||||||
|
smartContractPermissioningConfiguration.setNodeSmartContractInterfaceVersion( |
||||||
|
permissionsNodesContractVersion); |
||||||
|
} |
||||||
|
} else if (permissionsNodesContractAddress != null) { |
||||||
|
logger.warn( |
||||||
|
"Node permissioning smart contract address set {} but smart contract node permissioning is disabled.", |
||||||
|
permissionsNodesContractAddress); |
||||||
|
} |
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(permissionsAccountsContractEnabled)) { |
||||||
|
if (permissionsAccountsContractAddress == null) { |
||||||
|
throw new CommandLine.ParameterException( |
||||||
|
commandLine, |
||||||
|
"No account permissioning contract address specified. Cannot enable smart contract based account permissioning."); |
||||||
|
} else { |
||||||
|
smartContractPermissioningConfiguration.setSmartContractAccountAllowlistEnabled( |
||||||
|
permissionsAccountsContractEnabled); |
||||||
|
smartContractPermissioningConfiguration.setAccountSmartContractAddress( |
||||||
|
permissionsAccountsContractAddress); |
||||||
|
} |
||||||
|
} else if (permissionsAccountsContractAddress != null) { |
||||||
|
logger.warn( |
||||||
|
"Account permissioning smart contract address set {} but smart contract account permissioning is disabled.", |
||||||
|
permissionsAccountsContractAddress); |
||||||
|
} |
||||||
|
|
||||||
|
final PermissioningConfiguration permissioningConfiguration = |
||||||
|
new PermissioningConfiguration( |
||||||
|
localPermissioningConfigurationOptional, |
||||||
|
Optional.of(smartContractPermissioningConfiguration)); |
||||||
|
|
||||||
|
return Optional.of(permissioningConfiguration); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean localPermissionsEnabled() { |
||||||
|
return permissionsAccountsEnabled || permissionsNodesEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean contractPermissionsEnabled() { |
||||||
|
return permissionsNodesContractEnabled || permissionsAccountsContractEnabled; |
||||||
|
} |
||||||
|
|
||||||
|
private String getDefaultPermissioningFilePath(final Path dataPath) { |
||||||
|
return dataPath |
||||||
|
+ System.getProperty("file.separator") |
||||||
|
+ DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,453 @@ |
|||||||
|
/* |
||||||
|
* 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.cli.options; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
|
||||||
|
import org.hyperledger.besu.cli.CommandTestAbstract; |
||||||
|
import org.hyperledger.besu.datatypes.Address; |
||||||
|
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.LocalPermissioningConfiguration; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; |
||||||
|
import org.hyperledger.besu.ethereum.permissioning.SmartContractPermissioningConfiguration; |
||||||
|
import org.hyperledger.besu.plugin.data.EnodeURL; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.URL; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import com.google.common.io.Resources; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
import org.junit.jupiter.api.io.TempDir; |
||||||
|
import org.mockito.Mockito; |
||||||
|
import org.mockito.junit.jupiter.MockitoExtension; |
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class) |
||||||
|
public class PermissionsOptionsTest extends CommandTestAbstract { |
||||||
|
private static final String PERMISSIONING_CONFIG_TOML = "/permissioning_config.toml"; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void errorIsRaisedIfStaticNodesAreNotAllowed(final @TempDir Path testFolder) |
||||||
|
throws IOException { |
||||||
|
final Path staticNodesFile = testFolder.resolve("static-nodes.json"); |
||||||
|
final Path permissioningConfig = testFolder.resolve("permissioning.json"); |
||||||
|
|
||||||
|
final EnodeURL staticNodeURI = |
||||||
|
EnodeURLImpl.builder() |
||||||
|
.nodeId( |
||||||
|
"50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa") |
||||||
|
.ipAddress("127.0.0.1") |
||||||
|
.useDefaultPorts() |
||||||
|
.build(); |
||||||
|
|
||||||
|
final EnodeURL allowedNode = |
||||||
|
EnodeURLImpl.builder() |
||||||
|
.nodeId( |
||||||
|
"50203c6bfca6874370e71aecc8958529fd723feb05013dc1abca8fc1fff845c5259faba05852e9dfe5ce172a7d6e7c2a3a5eaa8b541c8af15ea5518bbff5f2fa") |
||||||
|
.useDefaultPorts() |
||||||
|
.ipAddress("127.0.0.1") |
||||||
|
.listeningPort(30304) |
||||||
|
.build(); |
||||||
|
|
||||||
|
Files.write(staticNodesFile, ("[\"" + staticNodeURI.toString() + "\"]").getBytes(UTF_8)); |
||||||
|
Files.write( |
||||||
|
permissioningConfig, |
||||||
|
("nodes-allowlist=[\"" + allowedNode.toString() + "\"]").getBytes(UTF_8)); |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--data-path=" + testFolder, |
||||||
|
"--bootnodes", |
||||||
|
"--permissions-nodes-config-file-enabled=true", |
||||||
|
"--permissions-nodes-config-file=" + permissioningConfig); |
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.contains(staticNodeURI.toString(), "not in nodes-allowlist"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsSmartContractWithoutOptionMustError() { |
||||||
|
parseCommand("--permissions-nodes-contract-address"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.startsWith("Missing required parameter for option '--permissions-nodes-contract-address'"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsEnabledWithoutContractAddressMustError() { |
||||||
|
parseCommand("--permissions-nodes-contract-enabled"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.contains("No node permissioning contract address specified"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsEnabledWithInvalidContractAddressMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-contract-enabled", |
||||||
|
"--permissions-nodes-contract-address", |
||||||
|
"invalid-smart-contract-address"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Invalid value"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsEnabledWithTooShortContractAddressMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-contract-enabled", "--permissions-nodes-contract-address", "0x1234"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Invalid value"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsSmartContractMustUseOption() { |
||||||
|
|
||||||
|
final String smartContractAddress = "0x0000000000000000000000000000000000001234"; |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-contract-enabled", |
||||||
|
"--permissions-nodes-contract-address", |
||||||
|
smartContractAddress); |
||||||
|
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration = |
||||||
|
new SmartContractPermissioningConfiguration(); |
||||||
|
smartContractPermissioningConfiguration.setNodeSmartContractAddress( |
||||||
|
Address.fromHexString(smartContractAddress)); |
||||||
|
smartContractPermissioningConfiguration.setSmartContractNodeAllowlistEnabled(true); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
final PermissioningConfiguration config = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(config.getSmartContractConfig().get()) |
||||||
|
.usingRecursiveComparison() |
||||||
|
.isEqualTo(smartContractPermissioningConfiguration); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsContractVersionDefaultValue() { |
||||||
|
final SmartContractPermissioningConfiguration expectedConfig = |
||||||
|
new SmartContractPermissioningConfiguration(); |
||||||
|
expectedConfig.setNodeSmartContractAddress( |
||||||
|
Address.fromHexString("0x0000000000000000000000000000000000001234")); |
||||||
|
expectedConfig.setSmartContractNodeAllowlistEnabled(true); |
||||||
|
expectedConfig.setNodeSmartContractInterfaceVersion(1); |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-contract-enabled", |
||||||
|
"--permissions-nodes-contract-address", |
||||||
|
"0x0000000000000000000000000000000000001234"); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
final PermissioningConfiguration config = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(config.getSmartContractConfig().get()) |
||||||
|
.usingRecursiveComparison() |
||||||
|
.isEqualTo(expectedConfig); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissionsContractVersionSetsValue() { |
||||||
|
final SmartContractPermissioningConfiguration expectedConfig = |
||||||
|
new SmartContractPermissioningConfiguration(); |
||||||
|
expectedConfig.setNodeSmartContractAddress( |
||||||
|
Address.fromHexString("0x0000000000000000000000000000000000001234")); |
||||||
|
expectedConfig.setSmartContractNodeAllowlistEnabled(true); |
||||||
|
expectedConfig.setNodeSmartContractInterfaceVersion(2); |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-contract-enabled", |
||||||
|
"--permissions-nodes-contract-address", |
||||||
|
"0x0000000000000000000000000000000000001234", |
||||||
|
"--permissions-nodes-contract-version", |
||||||
|
"2"); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
final PermissioningConfiguration config = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(config.getSmartContractConfig().get()) |
||||||
|
.usingRecursiveComparison() |
||||||
|
.isEqualTo(expectedConfig); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissionsSmartContractWithoutOptionMustError() { |
||||||
|
parseCommand("--permissions-accounts-contract-address"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.startsWith( |
||||||
|
"Missing required parameter for option '--permissions-accounts-contract-address'"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissionsEnabledWithoutContractAddressMustError() { |
||||||
|
parseCommand("--permissions-accounts-contract-enabled"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.contains("No account permissioning contract address specified"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissionsEnabledWithInvalidContractAddressMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-accounts-contract-enabled", |
||||||
|
"--permissions-accounts-contract-address", |
||||||
|
"invalid-smart-contract-address"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Invalid value"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissionsEnabledWithTooShortContractAddressMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-accounts-contract-enabled", |
||||||
|
"--permissions-accounts-contract-address", |
||||||
|
"0x1234"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Invalid value"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissionsSmartContractMustUseOption() { |
||||||
|
final String smartContractAddress = "0x0000000000000000000000000000000000001234"; |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--permissions-accounts-contract-enabled", |
||||||
|
"--permissions-accounts-contract-address", |
||||||
|
smartContractAddress); |
||||||
|
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration = |
||||||
|
new SmartContractPermissioningConfiguration(); |
||||||
|
smartContractPermissioningConfiguration.setAccountSmartContractAddress( |
||||||
|
Address.fromHexString(smartContractAddress)); |
||||||
|
smartContractPermissioningConfiguration.setSmartContractAccountAllowlistEnabled(true); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
final PermissioningConfiguration permissioningConfiguration = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(permissioningConfiguration.getSmartContractConfig()).isPresent(); |
||||||
|
|
||||||
|
final SmartContractPermissioningConfiguration effectiveSmartContractConfig = |
||||||
|
permissioningConfiguration.getSmartContractConfig().get(); |
||||||
|
assertThat(effectiveSmartContractConfig.isSmartContractAccountAllowlistEnabled()).isTrue(); |
||||||
|
assertThat(effectiveSmartContractConfig.getAccountSmartContractAddress()) |
||||||
|
.isEqualTo(Address.fromHexString(smartContractAddress)); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissioningTomlPathWithoutOptionMustDisplayUsage() { |
||||||
|
parseCommand("--permissions-nodes-config-file"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.startsWith("Missing required parameter for option '--permissions-nodes-config-file'"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissioningTomlPathWithoutOptionMustDisplayUsage() { |
||||||
|
parseCommand("--permissions-accounts-config-file"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)) |
||||||
|
.startsWith("Missing required parameter for option '--permissions-accounts-config-file'"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissioningEnabledWithNonexistentConfigFileMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-config-file-enabled", |
||||||
|
"--permissions-nodes-config-file", |
||||||
|
"file-does-not-exist"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Configuration file does not exist"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissioningEnabledWithNonexistentConfigFileMustError() { |
||||||
|
parseCommand( |
||||||
|
"--permissions-accounts-config-file-enabled", |
||||||
|
"--permissions-accounts-config-file", |
||||||
|
"file-does-not-exist"); |
||||||
|
|
||||||
|
Mockito.verifyNoInteractions(mockRunnerBuilder); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).contains("Configuration file does not exist"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissioningTomlFileWithNoPermissionsEnabledMustNotError() throws IOException { |
||||||
|
|
||||||
|
final URL configFile = this.getClass().getResource(PERMISSIONING_CONFIG_TOML); |
||||||
|
final Path permToml = createTempFile("toml", Resources.toByteArray(configFile)); |
||||||
|
parseCommand("--permissions-nodes-config-file", permToml.toString()); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissioningTomlFileWithNoPermissionsEnabledMustNotError() |
||||||
|
throws IOException { |
||||||
|
|
||||||
|
final URL configFile = this.getClass().getResource(PERMISSIONING_CONFIG_TOML); |
||||||
|
final Path permToml = createTempFile("toml", Resources.toByteArray(configFile)); |
||||||
|
parseCommand("--permissions-accounts-config-file", permToml.toString()); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void defaultPermissionsTomlFileWithNoPermissionsEnabledMustNotError() { |
||||||
|
parseCommand("--p2p-enabled", "false"); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).doesNotContain("no permissions enabled"); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void nodePermissioningTomlPathMustUseOption() throws IOException { |
||||||
|
final List<EnodeURL> allowedNodes = |
||||||
|
Lists.newArrayList( |
||||||
|
EnodeURLImpl.fromString( |
||||||
|
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567"), |
||||||
|
EnodeURLImpl.fromString( |
||||||
|
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.169.0.9:4568")); |
||||||
|
|
||||||
|
final URL configFile = this.getClass().getResource(PERMISSIONING_CONFIG_TOML); |
||||||
|
final Path permToml = createTempFile("toml", Resources.toByteArray(configFile)); |
||||||
|
|
||||||
|
final String allowedNodesString = |
||||||
|
allowedNodes.stream().map(Object::toString).collect(Collectors.joining(",")); |
||||||
|
parseCommand( |
||||||
|
"--permissions-nodes-config-file-enabled", |
||||||
|
"--permissions-nodes-config-file", |
||||||
|
permToml.toString(), |
||||||
|
"--bootnodes", |
||||||
|
allowedNodesString); |
||||||
|
final LocalPermissioningConfiguration localPermissioningConfiguration = |
||||||
|
LocalPermissioningConfiguration.createDefault(); |
||||||
|
localPermissioningConfiguration.setNodePermissioningConfigFilePath(permToml.toString()); |
||||||
|
localPermissioningConfiguration.setNodeAllowlist(allowedNodes); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
verify(mockRunnerBuilder).build(); |
||||||
|
|
||||||
|
final PermissioningConfiguration config = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(config.getLocalConfig().get()) |
||||||
|
.usingRecursiveComparison() |
||||||
|
.isEqualTo(localPermissioningConfiguration); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void accountPermissioningTomlPathMustUseOption() throws IOException { |
||||||
|
|
||||||
|
final URL configFile = this.getClass().getResource(PERMISSIONING_CONFIG_TOML); |
||||||
|
final Path permToml = createTempFile("toml", Resources.toByteArray(configFile)); |
||||||
|
|
||||||
|
parseCommand( |
||||||
|
"--permissions-accounts-config-file-enabled", |
||||||
|
"--permissions-accounts-config-file", |
||||||
|
permToml.toString()); |
||||||
|
final LocalPermissioningConfiguration localPermissioningConfiguration = |
||||||
|
LocalPermissioningConfiguration.createDefault(); |
||||||
|
localPermissioningConfiguration.setAccountPermissioningConfigFilePath(permToml.toString()); |
||||||
|
localPermissioningConfiguration.setAccountAllowlist( |
||||||
|
Collections.singletonList("0x0000000000000000000000000000000000000009")); |
||||||
|
|
||||||
|
verify(mockRunnerBuilder) |
||||||
|
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); |
||||||
|
final PermissioningConfiguration permissioningConfiguration = |
||||||
|
permissioningConfigurationArgumentCaptor.getValue().get(); |
||||||
|
assertThat(permissioningConfiguration.getLocalConfig()).isPresent(); |
||||||
|
|
||||||
|
final LocalPermissioningConfiguration effectiveLocalPermissioningConfig = |
||||||
|
permissioningConfiguration.getLocalConfig().get(); |
||||||
|
assertThat(effectiveLocalPermissioningConfig.isAccountAllowlistEnabled()).isTrue(); |
||||||
|
assertThat(effectiveLocalPermissioningConfig.getAccountPermissioningConfigFilePath()) |
||||||
|
.isEqualTo(permToml.toString()); |
||||||
|
|
||||||
|
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); |
||||||
|
assertThat(commandOutput.toString(UTF_8)).isEmpty(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue