com.yahoo.security.tls.json.TransportSecurityOptionsJsonSerializer Maven / Gradle / Ivy
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security.tls.json;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.security.tls.TransportSecurityOptions;
import com.yahoo.security.tls.json.TransportSecurityOptionsEntity.AuthorizedPeer;
import com.yahoo.security.tls.json.TransportSecurityOptionsEntity.CredentialField;
import com.yahoo.security.tls.json.TransportSecurityOptionsEntity.Files;
import com.yahoo.security.tls.json.TransportSecurityOptionsEntity.RequiredCredential;
import com.yahoo.security.tls.policy.AuthorizedPeers;
import com.yahoo.security.tls.policy.HostGlobPattern;
import com.yahoo.security.tls.policy.PeerPolicy;
import com.yahoo.security.tls.policy.RequiredPeerCredential;
import com.yahoo.security.tls.policy.Role;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* @author bjorncs
*/
public class TransportSecurityOptionsJsonSerializer {
private static final ObjectMapper mapper = new ObjectMapper();
public TransportSecurityOptions deserialize(InputStream in) {
try {
TransportSecurityOptionsEntity entity = mapper.readValue(in, TransportSecurityOptionsEntity.class);
return toTransportSecurityOptions(entity);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void serialize(OutputStream out, TransportSecurityOptions options) {
try {
mapper.writerWithDefaultPrettyPrinter().writeValue(out, toTransportSecurityOptionsEntity(options));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static TransportSecurityOptions toTransportSecurityOptions(TransportSecurityOptionsEntity entity) {
TransportSecurityOptions.Builder builder = new TransportSecurityOptions.Builder();
Files files = entity.files;
if (files != null) {
if (files.certificatesFile != null && files.privateKeyFile != null) {
builder.withCertificates(Paths.get(files.certificatesFile), Paths.get(files.privateKeyFile));
} else if (files.certificatesFile != null || files.privateKeyFile != null) {
throw new IllegalArgumentException("Both 'private-key' and 'certificates' must be configured together");
}
if (files.caCertificatesFile != null) {
builder.withCaCertificates(Paths.get(files.caCertificatesFile));
}
}
List authorizedPeersEntity = entity.authorizedPeers;
if (authorizedPeersEntity != null) {
if (authorizedPeersEntity.size() == 0) {
throw new IllegalArgumentException("'authorized-peers' cannot be empty");
}
builder.withAuthorizedPeers(new AuthorizedPeers(toPeerPolicies(authorizedPeersEntity)));
}
if (entity.acceptedCiphers != null) {
if (entity.acceptedCiphers.isEmpty()) {
throw new IllegalArgumentException("'accepted-ciphers' cannot be empty");
}
builder.withAcceptedCiphers(entity.acceptedCiphers);
}
return builder.build();
}
private static Set toPeerPolicies(List authorizedPeersEntity) {
return authorizedPeersEntity.stream()
.map(TransportSecurityOptionsJsonSerializer::toPeerPolicy)
.collect(toSet());
}
private static PeerPolicy toPeerPolicy(AuthorizedPeer authorizedPeer) {
if (authorizedPeer.name == null) {
throw missingFieldException("name");
}
if (authorizedPeer.requiredCredentials == null) {
throw missingFieldException("required-credentials");
}
return new PeerPolicy(authorizedPeer.name, toRoles(authorizedPeer.roles), toRequestPeerCredentials(authorizedPeer.requiredCredentials));
}
private static Set toRoles(List roles) {
if (roles == null) return Collections.emptySet();
return roles.stream()
.map(Role::new)
.collect(toSet());
}
private static List toRequestPeerCredentials(List requiredCredentials) {
return requiredCredentials.stream()
.map(TransportSecurityOptionsJsonSerializer::toRequiredPeerCredential)
.collect(toList());
}
private static RequiredPeerCredential toRequiredPeerCredential(RequiredCredential requiredCredential) {
if (requiredCredential.field == null) {
throw missingFieldException("field");
}
if (requiredCredential.matchExpression == null) {
throw missingFieldException("must-match");
}
return new RequiredPeerCredential(toField(requiredCredential.field), new HostGlobPattern(requiredCredential.matchExpression));
}
private static RequiredPeerCredential.Field toField(CredentialField field) {
switch (field) {
case CN: return RequiredPeerCredential.Field.CN;
case SAN_DNS: return RequiredPeerCredential.Field.SAN_DNS;
default: throw new IllegalArgumentException("Invalid field type: " + field);
}
}
private static TransportSecurityOptionsEntity toTransportSecurityOptionsEntity(TransportSecurityOptions options) {
TransportSecurityOptionsEntity entity = new TransportSecurityOptionsEntity();
entity.files = new Files();
options.getCaCertificatesFile().ifPresent(value -> entity.files.caCertificatesFile = value.toString());
options.getCertificatesFile().ifPresent(value -> entity.files.certificatesFile = value.toString());
options.getPrivateKeyFile().ifPresent(value -> entity.files.privateKeyFile = value.toString());
options.getAuthorizedPeers().ifPresent( authorizedPeers -> {
entity.authorizedPeers = new ArrayList<>();
for (PeerPolicy peerPolicy : authorizedPeers.peerPolicies()) {
AuthorizedPeer authorizedPeer = new AuthorizedPeer();
authorizedPeer.name = peerPolicy.policyName();
authorizedPeer.requiredCredentials = new ArrayList<>();
for (RequiredPeerCredential requiredPeerCredential : peerPolicy.requiredCredentials()) {
RequiredCredential requiredCredential = new RequiredCredential();
requiredCredential.field = toField(requiredPeerCredential.field());
requiredCredential.matchExpression = requiredPeerCredential.pattern().asString();
authorizedPeer.requiredCredentials.add(requiredCredential);
}
if (!peerPolicy.assumedRoles().isEmpty()) {
authorizedPeer.roles = new ArrayList<>();
peerPolicy.assumedRoles().forEach(role -> authorizedPeer.roles.add(role.name()));
}
entity.authorizedPeers.add(authorizedPeer);
}
});
if (!options.getAcceptedCiphers().isEmpty()) {
entity.acceptedCiphers = options.getAcceptedCiphers();
}
return entity;
}
private static CredentialField toField(RequiredPeerCredential.Field field) {
switch (field) {
case CN: return CredentialField.CN;
case SAN_DNS: return CredentialField.SAN_DNS;
default: throw new IllegalArgumentException("Invalid field type: " + field);
}
}
private static IllegalArgumentException missingFieldException(String fieldName) {
return new IllegalArgumentException(String.format("'%s' missing", fieldName));
}
}