Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Nc 2107 permissioning config toml file #643

Merged
merged 51 commits into from
Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c787dea
typo
macfarla Jan 22, 2019
6ce9630
typos
macfarla Jan 22, 2019
a614ffa
typo
macfarla Jan 22, 2019
bfdf859
create permissioning config from toml file
macfarla Jan 22, 2019
0239012
merged master
macfarla Jan 22, 2019
58bfa1b
fixed merge issue
macfarla Jan 22, 2019
cc38ef6
don't throw
macfarla Jan 22, 2019
260d417
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 23, 2019
426b644
parse TOML file for permissioning config
macfarla Jan 23, 2019
3aa9f1a
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 23, 2019
c5e5516
typo
macfarla Jan 22, 2019
802279f
typos
macfarla Jan 22, 2019
6c8dcdb
typo
macfarla Jan 22, 2019
a5a1dac
create permissioning config from toml file
macfarla Jan 22, 2019
3e77b52
don't throw
macfarla Jan 22, 2019
c89768f
parse TOML file for permissioning config
macfarla Jan 23, 2019
430b010
Merge branch 'nc-2107-permissioning-config-toml-file' of github.com:m…
macfarla Jan 23, 2019
3aeb719
added a separate non-CLI toml parser
macfarla Jan 24, 2019
3aad74e
formatting
macfarla Jan 24, 2019
5fafd91
added tests
macfarla Jan 24, 2019
6944ec1
formatting
macfarla Jan 24, 2019
fcfca80
avoid 2 x log messages
macfarla Jan 24, 2019
8a6ee0f
ignore tests
macfarla Jan 24, 2019
10d12d0
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 24, 2019
d5b7739
added CLI options for separately enabling node and account permissioning
macfarla Jan 25, 2019
7e2398a
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 25, 2019
5d9f833
tidying up
macfarla Jan 25, 2019
73c8ed5
handle when default permissions config file does not exist
macfarla Jan 25, 2019
b9dce8f
handle when default permissions config file does not exist
macfarla Jan 25, 2019
6e966d8
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 25, 2019
a46fc2f
added tests
macfarla Jan 25, 2019
cfb5531
moved check for bootnodes on whitelist out of CLI
macfarla Jan 26, 2019
2867d41
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 26, 2019
5d7296c
added tests
macfarla Jan 26, 2019
2542d61
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 29, 2019
39876ed
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 29, 2019
5980e2a
added test and set to empty list if toml key/value is absent
macfarla Jan 29, 2019
d45e2de
PR comments
macfarla Jan 29, 2019
586a820
PR comments
macfarla Jan 29, 2019
f29d7da
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 29, 2019
2395ea7
copied code to convert from Peer to enode format
macfarla Jan 29, 2019
81fb444
missed a
macfarla Jan 29, 2019
b08f6ca
Merge branch 'master' into nc-2107-permissioning-config-toml-file
mark-terry Jan 29, 2019
fc440e3
Merge branch 'master' of github.com:PegaSysEng/pantheon into nc-2107-…
macfarla Jan 29, 2019
dc26bdc
Merge branch 'nc-2107-permissioning-config-toml-file' of github.com:m…
macfarla Jan 29, 2019
9f620ae
Merge branch 'master' into nc-2107-permissioning-config-toml-file
mark-terry Jan 29, 2019
4ab5799
Merge branch 'master' into nc-2107-permissioning-config-toml-file
mark-terry Jan 29, 2019
2a10a44
Merge branch 'master' into nc-2107-permissioning-config-toml-file
macfarla Jan 29, 2019
0ce41e7
Merge branch 'master' into nc-2107-permissioning-config-toml-file
macfarla Jan 29, 2019
2619368
Merge branch 'master' into nc-2107-permissioning-config-toml-file
macfarla Jan 29, 2019
3302150
Merge branch 'master' into nc-2107-permissioning-config-toml-file
macfarla Jan 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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;

import static java.nio.charset.StandardCharsets.UTF_8;

import tech.pegasys.pantheon.cli.custom.EnodeToURIPropertyConverter;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.io.Resources;
import net.consensys.cava.toml.TomlArray;
import net.consensys.cava.toml.TomlParseResult;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class PermissioningConfigurationBuilder {

private static final Logger LOG = LogManager.getLogger();

// will be used to reload the config from a file while node is running
public static PermissioningConfiguration permissioningConfigurationFromToml(
final String configFilePath,
final boolean permissionedNodeEnabled,
final boolean permissionedAccountEnabled) {

String permToml = permissioningConfigTomlAsString(permissioningConfigFile(configFilePath));
return permissioningConfiguration(
TomlConfigFileParser.loadConfiguration(permToml),
permissionedNodeEnabled,
permissionedAccountEnabled);
}

public static PermissioningConfiguration permissioningConfiguration(
final TomlParseResult permToml,
final boolean permissionedNodeEnabled,
final boolean permissionedAccountEnabled) {

TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist");
TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist");

final PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
if (permissionedAccountEnabled) {
if (accountWhitelistTomlArray != null) {
List<String> accountsWhitelistToml =
accountWhitelistTomlArray
.toList()
.stream()
.map(Object::toString)
.collect(Collectors.toList());
permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml);
} else {
permissioningConfiguration.setAccountWhitelist(new ArrayList<>());
}
}
if (permissionedNodeEnabled) {
if (nodeWhitelistTomlArray != null) {
List<URI> nodesWhitelistToml =
nodeWhitelistTomlArray
.toList()
.stream()
.map(Object::toString)
.map(EnodeToURIPropertyConverter::convertToURI)
.collect(Collectors.toList());
permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml);
} else {
permissioningConfiguration.setNodeWhitelist(new ArrayList<>());
}
}
return permissioningConfiguration;
}

private static String permissioningConfigTomlAsString(final File file) {
try {
return Resources.toString(file.toURI().toURL(), UTF_8);
} catch (final Exception e) {
LOG.error("Unable to load permissioning config {}.", file, e);
return null;
}
}

private static File permissioningConfigFile(final String filename) {

final File permissioningConfigFile = new File(filename);
if (permissioningConfigFile.exists()) {
return permissioningConfigFile;
} else {
LOG.error("File does not exist: permissioning config path: {}.", filename);
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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;

import java.util.stream.Collectors;

import net.consensys.cava.toml.Toml;
import net.consensys.cava.toml.TomlParseError;
import net.consensys.cava.toml.TomlParseResult;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class TomlConfigFileParser {

private static final Logger LOG = LogManager.getLogger();

private static TomlParseResult checkConfigurationValidity(
final TomlParseResult result, final String toml) {
if (result == null || result.isEmpty()) {
LOG.error("Unable to read TOML configuration file %s", toml);
}
return result;
}

public static TomlParseResult loadConfiguration(final String toml) {
TomlParseResult result = Toml.parse(toml);

if (result.hasErrors()) {
final String errors =
result.errors().stream().map(TomlParseError::toString).collect(Collectors.joining("%n"));
;
LOG.error("Invalid TOML configuration : %s", errors);
}

return checkConfigurationValidity(result, toml);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ConfigOptionSearchAndRunHandler extends AbstractParseResultHandler<List<Ob
this.configFileOptionName = configFileOptionName;
this.isDocker = isDocker;
// use the same output as the regular options handler to ensure that outputs are all going
// the in the same place. No need to do this for the exception handler as we reuse it directly.
// in the same place. No need to do this for the exception handler as we reuse it directly.
this.useOut(resultHandler.out());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface DefaultCommandValues {
long DEFAULT_MAX_REFRESH_DELAY = 3600000;
long DEFAULT_MIN_REFRESH_DELAY = 1;
String DOCKER_GENESIS_LOCATION = "/etc/pantheon/genesis.json";
String PERMISSIONING_CONFIG_LOCATION = "/etc/pantheon/permissioned_config.toml";
String DOCKER_DATADIR_LOCATION = "/var/lib/pantheon";
String MANDATORY_HOST_FORMAT_HELP = "<HOST>";
String MANDATORY_PORT_FORMAT_HELP = "<PORT>";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.createDefault;

import tech.pegasys.pantheon.PermissioningConfigurationBuilder;
import tech.pegasys.pantheon.Runner;
import tech.pegasys.pantheon.RunnerBuilder;
import tech.pegasys.pantheon.cli.custom.CorsAllowedOriginsProperty;
Expand All @@ -44,15 +45,13 @@
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration;
import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.PrometheusMetricsSystem;
import tech.pegasys.pantheon.util.BlockImporter;
import tech.pegasys.pantheon.util.InvalidConfigurationException;
import tech.pegasys.pantheon.util.PermissioningConfigurationValidator;
import tech.pegasys.pantheon.util.bytes.BytesValue;

import java.io.File;
Expand All @@ -66,7 +65,6 @@
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.io.Resources;
Expand Down Expand Up @@ -457,6 +455,18 @@ private Long configureRefreshDelay(final Long refreshDelay) {
)
private final BytesValue extraData = DEFAULT_EXTRA_DATA;

@Option(
names = {"--permissions-nodes-enabled"},
description = "Set if node level permissions should be enabled (default: ${DEFAULT-VALUE})"
)
private final Boolean permissionsNodesEnabled = false;

@Option(
names = {"--permissions-accounts-enabled"},
description = "Set if account level permissions should be enabled (default: ${DEFAULT-VALUE})"
)
private final Boolean permissionsAccountsEnabled = false;

// Permissioning: A list of whitelist nodes can be passed.
@Option(
names = {"--nodes-whitelist"},
Expand Down Expand Up @@ -600,23 +610,11 @@ private NetworkName getNetwork() {
private void ensureAllBootnodesAreInWhitelist(
final EthNetworkConfig ethNetworkConfig,
final PermissioningConfiguration permissioningConfiguration) {
final List<Peer> bootnodes =
DiscoveryConfiguration.getBootstrapPeersFromGenericCollection(
ethNetworkConfig.getBootNodes());
if (permissioningConfiguration.isNodeWhitelistSet() && bootnodes != null) {
final List<Peer> whitelist =
permissioningConfiguration
.getNodeWhitelist()
.stream()
.map(DefaultPeer::fromURI)
.collect(Collectors.toList());
for (final Peer bootnode : bootnodes) {
if (!whitelist.contains(bootnode)) {
throw new ParameterException(
new CommandLine(this),
"Cannot start node with bootnode(s) that are not in nodes-whitelist " + bootnode);
}
}
try {
PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist(
ethNetworkConfig, permissioningConfiguration);
} catch (Exception e) {
throw new ParameterException(new CommandLine(this), e.getMessage());
}
}

Expand Down Expand Up @@ -681,10 +679,16 @@ MetricsConfiguration metricsConfiguration() {
}

private PermissioningConfiguration permissioningConfiguration() {

if (!permissionsAccountsEnabled && !permissionsNodesEnabled) {
return PermissioningConfiguration.createDefault();
}

final PermissioningConfiguration permissioningConfiguration =
PermissioningConfiguration.createDefault();
permissioningConfiguration.setNodeWhitelist(nodesWhitelist);
permissioningConfiguration.setAccountWhitelist(accountsWhitelist);
PermissioningConfigurationBuilder.permissioningConfigurationFromToml(
DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION,
permissionsNodesEnabled,
permissionsAccountsEnabled);
return permissioningConfiguration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public class EnodeToURIPropertyConverter implements ITypeConverter<URI> {

@Override
public URI convert(final String value) throws IllegalArgumentException {
return convertToURI(value);
}

public static URI convertToURI(final String value) throws IllegalArgumentException {
checkArgument(
value != null && !value.isEmpty(), "Can't convert null/empty string to EnodeURLProperty.");

Expand Down Expand Up @@ -85,7 +89,7 @@ public URI convert(final String value) throws IllegalArgumentException {
}
}

private String getAndValidateNodeId(final Matcher matcher) {
private static String getAndValidateNodeId(final Matcher matcher) {
final String invalidNodeIdErrorMsg =
"Enode URL contains an invalid node ID. Node ID must have 128 characters and shouldn't include the '0x' hex prefix.";
final String nodeId = matcher.group("nodeId");
Expand All @@ -96,15 +100,15 @@ private String getAndValidateNodeId(final Matcher matcher) {
return nodeId;
}

private Integer getAndValidatePort(final Matcher matcher, final String portName) {
private static Integer getAndValidatePort(final Matcher matcher, final String portName) {
int port = Integer.valueOf(matcher.group(portName));
checkArgument(
NetworkUtility.isValidPort(port),
"Invalid " + portName + " port range. Port should be between 0 - 65535");
return port;
}

private boolean containsDiscoveryPort(final String value) {
private static boolean containsDiscoveryPort(final String value) {
return value.contains("discport");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.util;

import tech.pegasys.pantheon.cli.EthNetworkConfig;
import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration;
import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint;
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.Collectors;

import org.bouncycastle.util.encoders.Hex;

public class PermissioningConfigurationValidator {

public static void areAllBootnodesAreInWhitelist(
final EthNetworkConfig ethNetworkConfig,
final PermissioningConfiguration permissioningConfiguration)
throws Exception {
List<Peer> bootnodesNotInWhitelist = new ArrayList<>();
final List<Peer> bootnodes =
DiscoveryConfiguration.getBootstrapPeersFromGenericCollection(
ethNetworkConfig.getBootNodes());
if (permissioningConfiguration.isNodeWhitelistSet() && bootnodes != null) {
bootnodesNotInWhitelist =
bootnodes
.stream()
.filter(
node ->
!permissioningConfiguration
.getNodeWhitelist()
.contains(URI.create(buildEnodeURI(node))))
.collect(Collectors.toList());
}
if (!bootnodesNotInWhitelist.isEmpty()) {
throw new Exception("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist);
}
}

private static String buildEnodeURI(final Peer s) {
String url = Hex.toHexString(s.getId().extractArray());
Endpoint endpoint = s.getEndpoint();
String nodeIp = endpoint.getHost();
OptionalInt tcpPort = endpoint.getTcpPort();
int udpPort = endpoint.getUdpPort();

if (tcpPort.isPresent() && (tcpPort.getAsInt() != udpPort)) {
return String.format(
"enode://%s@%s:%d?discport=%d", url, nodeIp, tcpPort.getAsInt(), udpPort);
} else {
return String.format("enode://%s@%s:%d", url, nodeIp, udpPort);
}
}
}
Loading