Skip to content

Commit

Permalink
[FABJ-521] Allow arrays of PEM content in connection profile (#63)
Browse files Browse the repository at this point in the history
The connection profile tlsCaCerts element has an optional "pem" property containing one or more PEM certificates. This change allows the "pem" property to be either a string or an array of strings. For arrays, all of the contained strings are concatenated to produce the same result as including multiple certificates in a single string.

Signed-off-by: Mark S. Lewis <[email protected]>
  • Loading branch information
bestbeforetoday authored Jul 3, 2020
1 parent bcea80a commit 04508d9
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 122 deletions.
163 changes: 64 additions & 99 deletions src/main/java/org/hyperledger/fabric/sdk/NetworkConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -31,10 +32,12 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import java.util.function.Function;
import java.util.stream.Collectors;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
Expand Down Expand Up @@ -70,10 +73,11 @@
*/

public class NetworkConfig {
private static final String URL_PROP_NAME = "url";

private final JsonObject jsonConfig;

private OrgInfo clientOrganization;
private final OrgInfo clientOrganization;

private Map<String, Node> orderers;
private Map<String, Node> peers;
Expand All @@ -86,7 +90,7 @@ public class NetworkConfig {
*/
public Collection<String> getPeerNames() {
if (peers == null) {
return Collections.EMPTY_SET;
return Collections.emptySet();
} else {
return new HashSet<>(peers.keySet());
}
Expand All @@ -99,7 +103,7 @@ public Collection<String> getPeerNames() {
*/
public Collection<String> getOrdererNames() {
if (orderers == null) {
return Collections.EMPTY_SET;
return Collections.emptySet();
} else {
return new HashSet<>(orderers.keySet());
}
Expand All @@ -113,7 +117,7 @@ public Collection<String> getOrdererNames() {

public Collection<String> getEventHubNames() {
if (eventHubs == null) {
return Collections.EMPTY_SET;
return Collections.emptySet();
} else {
return new HashSet<>(eventHubs.keySet());
}
Expand Down Expand Up @@ -404,14 +408,10 @@ private static NetworkConfig fromFile(File configFile, boolean isJson) throws In
logger.trace(format("NetworkConfig.fromFile: %s isJson = %b", configFile.getAbsolutePath(), isJson));
}

NetworkConfig config;

// Json file
try (InputStream stream = new FileInputStream(configFile)) {
config = isJson ? fromJsonStream(stream) : fromYamlStream(stream);
return isJson ? fromJsonStream(stream) : fromYamlStream(stream);
}

return config;
}

/**
Expand Down Expand Up @@ -484,43 +484,26 @@ Channel loadChannel(HFClient client, String channelName) throws NetworkConfigura
logger.trace(format("NetworkConfig.loadChannel: %s", channelName));
}

Channel channel = null;

JsonObject channels = getJsonObject(jsonConfig, "channels");

if (channels != null) {
JsonObject jsonChannel = getJsonObject(channels, channelName);
if (jsonChannel != null) {
channel = client.getChannel(channelName);
if (channel != null) {
// The channel already exists in the client!
// Note that by rights this should never happen as HFClient.loadChannelFromConfig should have already checked for this!
throw new NetworkConfigurationException(format("Channel %s is already configured in the client!", channelName));
}
channel = reconstructChannel(client, channelName, jsonChannel);
} else {

final Set<String> channelNames = getChannelNames();
if (channelNames.isEmpty()) {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}
final StringBuilder sb = new StringBuilder(1000);

channelNames.forEach(s -> {
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(s);
});
throw new NetworkConfigurationException(format("Channel %s not found in configuration file. Found channel names: %s ", channelName, sb.toString()));

JsonObject jsonChannel = getJsonObject(channels, channelName);
if (null == jsonChannel) {
final Set<String> channelNames = getChannelNames();
if (channelNames.isEmpty()) {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
}
throw new NetworkConfigurationException(format("Channel %s not found in configuration file. Found channel names: %s ",
channelName, String.join(", ", channelNames)));
}

} else {
throw new NetworkConfigurationException("Channel configuration has no channels defined.");
Channel channel = client.getChannel(channelName);
if (channel != null) {
// The channel already exists in the client!
// Note that by rights this should never happen as HFClient.loadChannelFromConfig should have already checked for this!
throw new NetworkConfigurationException(format("Channel %s is already configured in the client!", channelName));
}

return channel;
return reconstructChannel(client, channelName, jsonChannel);
}

// Creates Node instances representing all the orderers defined in the config file
Expand All @@ -546,7 +529,7 @@ private void createAllOrderers() throws NetworkConfigurationException {
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
}

Node orderer = createNode(ordererName, jsonOrderer, "url");
Node orderer = createNode(ordererName, jsonOrderer, URL_PROP_NAME);
if (orderer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid orderer entry: %s", ordererName));
}
Expand Down Expand Up @@ -585,7 +568,7 @@ private void createAllPeers() throws NetworkConfigurationException {
throw new NetworkConfigurationException(format("Error loading config. Invalid peer entry: %s", peerName));
}

Node peer = createNode(peerName, jsonPeer, "url");
Node peer = createNode(peerName, jsonPeer, URL_PROP_NAME);
if (peer == null) {
throw new NetworkConfigurationException(format("Error loading config. Invalid peer entry: %s", peerName));
}
Expand Down Expand Up @@ -753,11 +736,10 @@ private static void setPeerRole(String channelName, PeerOptions peerOptions, Jso
}
}

private static Map<PeerRole, String> roleNameRemapHash = new HashMap<PeerRole, String>() {
{
put(PeerRole.SERVICE_DISCOVERY, "discover");
}
};
private static final Map<PeerRole, String> roleNameRemapHash = new HashMap<>();
static {
roleNameRemapHash.put(PeerRole.SERVICE_DISCOVERY, "discover");
}

private static String roleNameRemap(PeerRole peerRole) {
String remap = roleNameRemapHash.get(peerRole);
Expand All @@ -776,12 +758,6 @@ private Orderer getOrderer(HFClient client, String ordererName) throws InvalidAr

// Creates a new Node instance from a JSON object
private Node createNode(String nodeName, JsonObject jsonNode, String urlPropName) throws NetworkConfigurationException {

// jsonNode.
// if (jsonNode.isNull(urlPropName)) {
// return null;
// }

String url = jsonNode.getString(urlPropName, null);
if (url == null) {
return null;
Expand Down Expand Up @@ -813,16 +789,15 @@ private void getTLSCerts(String nodeName, JsonObject jsonOrderer, Properties pro
JsonObject jsonTlsCaCerts = getJsonObject(jsonOrderer, "tlsCACerts");
if (jsonTlsCaCerts != null) {
String pemFilename = getJsonValueAsString(jsonTlsCaCerts.get("path"));
String pemBytes = getJsonValueAsString(jsonTlsCaCerts.get("pem"));

if (pemFilename != null) {
// let the sdk handle non existing errors could be they don't exist during parsing but are there later.
props.put("pemFile", pemFilename);
}

if (pemBytes != null) {
props.put("pemBytes", pemBytes.getBytes());
}
byte[] pemBytes = getJsonValueAsList(jsonTlsCaCerts.get("pem"), NetworkConfig::getJsonValueAsString).stream()
.collect(Collectors.joining())
.getBytes();
props.put("pemBytes", pemBytes);
}
}

Expand Down Expand Up @@ -878,24 +853,20 @@ private OrgInfo createOrg(String orgName, JsonObject jsonOrg, Map<String, JsonOb
String signedCert = extractPemString(jsonOrg, "signedCert", msgPrefix);

if (!isNullOrEmpty(adminPrivateKeyString) && !isNullOrEmpty(signedCert)) {

PrivateKey privateKey = null;

final PrivateKey privateKey;
try {
privateKey = getPrivateKeyFromString(adminPrivateKeyString);
} catch (IOException ioe) {
throw new NetworkConfigurationException(format("%s: Invalid private key", msgPrefix), ioe);
}

final PrivateKey privateKeyFinal = privateKey;

try {
org.peerAdmin = new UserInfo(CryptoSuite.Factory.getCryptoSuite(), mspId, "PeerAdmin_" + mspId + "_" + orgName, null);
} catch (Exception e) {
throw new NetworkConfigurationException(e.getMessage(), e);
}
org.peerAdmin.setEnrollment(new X509Enrollment(privateKeyFinal, signedCert));

org.peerAdmin.setEnrollment(new X509Enrollment(privateKey, signedCert));
}

return org;
Expand Down Expand Up @@ -938,7 +909,7 @@ private static String extractPemString(JsonObject json, String fieldName, String
throw new NetworkConfigurationException(format("%s: %s file %s does not exist", msgPrefix, fieldName, fullPathname));
}
try (FileInputStream stream = new FileInputStream(pemFile)) {
pemString = IOUtils.toString(stream, "UTF-8");
pemString = IOUtils.toString(stream, StandardCharsets.UTF_8);
} catch (IOException ioe) {
throw new NetworkConfigurationException(format("Failed to read file: %s", fullPathname), ioe);
}
Expand All @@ -954,21 +925,15 @@ private CAInfo createCA(String name, JsonObject jsonCA, OrgInfo org) throws Netw
String url = getJsonValueAsString(jsonCA.get("url"));
Properties httpOptions = extractProperties(jsonCA, "httpOptions");

String enrollId = null;
String enrollSecret = null;

List<JsonObject> registrars = getJsonValueAsList(jsonCA.get("registrar"));
List<UserInfo> regUsers = new LinkedList<>();
if (registrars != null) {

for (JsonObject reg : registrars) {
enrollId = getJsonValueAsString(reg.get("enrollId"));
enrollSecret = getJsonValueAsString(reg.get("enrollSecret"));
try {
regUsers.add(new UserInfo(CryptoSuite.Factory.getCryptoSuite(), org.mspId, enrollId, enrollSecret));
} catch (Exception e) {
throw new NetworkConfigurationException(e.getMessage(), e);
}
List<UserInfo> regUsers = new ArrayList<>();
List<JsonObject> registrars = getJsonValueAsList(jsonCA.get("registrar"), NetworkConfig::getJsonValueAsObject);
for (JsonObject registrar : registrars) {
String enrollId = getJsonValueAsString(registrar.get("enrollId"));
String enrollSecret = getJsonValueAsString(registrar.get("enrollSecret"));
try {
regUsers.add(new UserInfo(CryptoSuite.Factory.getCryptoSuite(), org.mspId, enrollId, enrollSecret));
} catch (Exception e) {
throw new NetworkConfigurationException(e.getMessage(), e);
}
}

Expand Down Expand Up @@ -1058,20 +1023,22 @@ private static JsonArray getJsonValueAsArray(JsonValue value) {
return (value != null && value.getValueType() == ValueType.ARRAY) ? value.asJsonArray() : null;
}

// Returns the specified JsonValue as a List. Allows single or array
private static List<JsonObject> getJsonValueAsList(JsonValue value) {
if (value != null) {
if (value.getValueType() == ValueType.ARRAY) {
return value.asJsonArray().getValuesAs(JsonObject.class);

} else if (value.getValueType() == ValueType.OBJECT) {
List<JsonObject> ret = new ArrayList<>();
ret.add(value.asJsonObject());
private static <T> List<T> getJsonValueAsList(final JsonValue value, Function<JsonValue, T> map) {
return getJsonValueAsList(value).stream()
.map(map)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

return ret;
}
// Returns the specified JsonValue as a List. Allows single or array
private static List<JsonValue> getJsonValueAsList(final JsonValue value) {
if (value == null) {
return Collections.emptyList();
}
return null;
if (value.getValueType() == ValueType.ARRAY) {
return value.asJsonArray();
}
return Collections.singletonList(value);
}

// Returns the specified JsonValue as a String, or null if it's not a string
Expand Down Expand Up @@ -1113,16 +1080,14 @@ private static JsonObject getJsonObject(JsonObject object, String propName) {
*/

public Set<String> getChannelNames() {
Set<String> ret = Collections.EMPTY_SET;
Set<String> result = new HashSet<>();

JsonObject channels = getJsonObject(jsonConfig, "channels");
if (channels != null) {
final Set<String> channelNames = channels.keySet();
if (channelNames != null && !channelNames.isEmpty()) {
ret = new HashSet<>(channelNames);
}
result.addAll(channels.keySet());
}
return ret;

return result;
}

// Holds a network "node" (eg. Peer, Orderer, EventHub)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
"mspid": "Org2MSP",
"peers": [
"peer0.org2.example.com"
],
"certificateAuthorities": [
"ca-org2"
]
}
},
Expand Down Expand Up @@ -120,7 +123,27 @@
"verify": true
},
"tlsCACerts": {
"path": "peerOrganizations/org1.example.com/ca/org1.example.com-cert.pem"
"path": "peerOrganizations/org1.example.com/ca/org1.example.com-cert.pem",
"pem": "-----BEGIN CERTIFICATE----- <etc>"
},
"registrar": [
{
"enrollId": "admin",
"enrollSecret": "adminpw"
}
],
"caName": "caNameHere"
},
"ca-org2": {
"url": "https://localhost:8054",
"httpOptions": {
"verify": true
},
"tlsCACerts": {
"pem": [
"-----BEGIN CERTIFICATE----- <1>",
"-----BEGIN CERTIFICATE----- <2>"
]
},
"registrar": [
{
Expand All @@ -131,4 +154,4 @@
"caName": "caNameHere"
}
}
}
}
Loading

0 comments on commit 04508d9

Please sign in to comment.