Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
TT-1211: upgrading dropwizard
Browse files Browse the repository at this point in the history
As part of the upgrade, jackson has upgraded to 2.8
They over-eagerly prevent our polymorphism (FasterXML/jackson-databind#1358)
so we have to use custom deserialisers for the interfaces

Solo: @hugh-emerson
  • Loading branch information
Hugh Emerson committed Sep 26, 2017
1 parent 6791713 commit cf8d90f
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package uk.gov.ida.common.shared.configuration;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.dropwizard.jackson.Discoverable;

import java.security.PublicKey;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonDeserialize(using=DeserializablePublicKeyConfigurationDeserializer.class)
public interface DeserializablePublicKeyConfiguration extends Discoverable {
PublicKey getPublicKey();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package uk.gov.ida.common.shared.configuration;


import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class DeserializablePublicKeyConfigurationDeserializer extends JsonDeserializer<DeserializablePublicKeyConfiguration> {
@Override
public DeserializablePublicKeyConfiguration deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// Setting the Codec explicitly is needed when this executes with the YAMLParser
// for example, when our Dropwizard apps start. The codec doesn't need to be set
// when the JsonParser implementation is used.
p.setCodec(new ObjectMapper());
JsonNode node = p.getCodec().readTree(p);

JsonNode certFileNode = node.get("certFile");
if(certFileNode != null) {
return p.getCodec().treeToValue(node, PublicKeyFileConfiguration.class);
}
return p.getCodec().treeToValue(node, EncodedCertificateConfiguration.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.security.PublicKey;

@JsonDeserialize(using=EncodedCertificateDeserializer.class)
@JsonTypeName("base64")
public class EncodedCertificateConfiguration implements DeserializablePublicKeyConfiguration {
private PublicKey publicKey;
private String cert;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

@SuppressWarnings("unused")
@JsonDeserialize(using=EncodedPrivateKeyDeserializer.class)
@JsonTypeName("base64")
public class EncodedPrivateKeyConfiguration implements PrivateKeyConfiguration {

public EncodedPrivateKeyConfiguration(PrivateKey privateKey, String key) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package uk.gov.ida.common.shared.configuration;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.dropwizard.jackson.Discoverable;
import java.security.PrivateKey;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonDeserialize(using=PrivateKeyConfigurationDeserializer.class)
public interface PrivateKeyConfiguration extends Discoverable {
PrivateKey getPrivateKey();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uk.gov.ida.common.shared.configuration;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class PrivateKeyConfigurationDeserializer extends JsonDeserializer<PrivateKeyConfiguration> {
@Override
public PrivateKeyConfiguration deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// Setting the Codec explicitly is needed when this executes with the YAMLParser
// for example, when our Dropwizard apps start. The codec doesn't need to be set
// when the JsonParser implementation is used.
p.setCodec(new ObjectMapper());
JsonNode node = p.getCodec().readTree(p);

JsonNode certFileNode = node.get("keyFile");
if(certFileNode != null) {
return p.getCodec().treeToValue(node, PrivateKeyFileConfiguration.class);
}
return p.getCodec().treeToValue(node, EncodedPrivateKeyConfiguration.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

@SuppressWarnings("unused")
@JsonDeserialize(using=PrivateKeyFileDeserializer.class)
@JsonTypeName("file")
public class PrivateKeyFileConfiguration implements PrivateKeyConfiguration {

public PrivateKeyFileConfiguration(PrivateKey privateKey, String keyFile) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package uk.gov.ida.common.shared.configuration;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.security.PublicKey;

@JsonDeserialize(using=PublicKeyDeserializer.class)
@JsonTypeName("file")
@JsonDeserialize(using=PublicKeyFileConfigurationDeserializer.class)
public class PublicKeyFileConfiguration implements DeserializablePublicKeyConfiguration {
private PublicKey publicKey;
private String cert;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.security.PublicKey;
import java.security.cert.Certificate;

public class PublicKeyDeserializer extends JsonDeserializer<PublicKeyFileConfiguration> {
public class PublicKeyFileConfigurationDeserializer extends JsonDeserializer<PublicKeyFileConfiguration> {
@Override
public PublicKeyFileConfiguration deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// Setting the Codec explicitly is needed when this executes with the YAMLParser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void should_loadPublicKeyFromJSON() throws Exception {
String path = Resources.getResource("public_key.crt").getFile();
byte[] cert = Files.readAllBytes(new File(path).toPath());
String encodedCert = Base64.getEncoder().encodeToString(cert);
String jsonConfig = "{\"type\": \"base64\", \"cert\": \"" + encodedCert + "\", \"name\": \"someId\"}";
String jsonConfig = "{\"cert\": \"" + encodedCert + "\", \"name\": \"someId\"}";
EncodedCertificateConfiguration config = objectMapper.readValue(jsonConfig, EncodedCertificateConfiguration.class);

assertThat(config.getPublicKey().getAlgorithm()).isEqualTo("RSA");
Expand All @@ -38,20 +38,20 @@ public void should_ThrowExceptionWhenStringDoesNotContainAPublicKey() throws Exc
String path = Resources.getResource("private_key.pk8").getFile();
byte[] key = Files.readAllBytes(new File(path).toPath());
String encodedKey = Base64.getEncoder().encodeToString(key);
objectMapper.readValue("{\"type\": \"base64\", \"cert\": \"" + encodedKey + "\", \"name\": \"someId\"}", EncodedCertificateConfiguration.class);
objectMapper.readValue("{\"cert\": \"" + encodedKey + "\", \"name\": \"someId\"}", EncodedCertificateConfiguration.class);
}

@Test
public void should_ThrowExceptionWhenStringIsNotBase64Encoded() throws Exception {
thrown.expect(IllegalArgumentException.class);

objectMapper.readValue("{\"type\": \"base64\", \"cert\": \"" + "FOOBARBAZ" + "\", \"name\": \"someId\"}", EncodedCertificateConfiguration.class);
objectMapper.readValue("{\"cert\": \"" + "FOOBARBAZ" + "\", \"name\": \"someId\"}", EncodedCertificateConfiguration.class);
}

@Test(expected = IllegalStateException.class)
public void should_ThrowExceptionWhenIncorrectKeySpecified() throws Exception {
String path = getClass().getClassLoader().getResource("empty_file").getPath();
String jsonConfig = "{\"type\": \"base64\", \"certFileFoo\": \"" + path + "\", \"name\": \"someId\"}";
String jsonConfig = "{\"certFileFoo\": \"" + path + "\", \"name\": \"someId\"}";
objectMapper.readValue(jsonConfig, EncodedCertificateConfiguration.class);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.ida.common.shared.configuration;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;
import org.junit.Rule;
Expand All @@ -25,7 +26,7 @@ public void should_loadPrivateKeyFromJSON() throws Exception {
String path = Resources.getResource("private_key.pk8").getFile();
byte[] key = Files.readAllBytes(new File(path).toPath());
String encodedKey = Base64.getEncoder().encodeToString(key);
String jsonConfig = "{\"type\": \"base64\", \"key\": \"" + encodedKey + "\"}";
String jsonConfig = "{\"key\": \"" + encodedKey + "\"}";
EncodedPrivateKeyConfiguration configuration = objectMapper.readValue(jsonConfig, EncodedPrivateKeyConfiguration.class);
assertThat(configuration.getPrivateKey().getAlgorithm()).isEqualTo("RSA");
}
Expand All @@ -36,11 +37,11 @@ public void should_ThrowFooExceptionWhenKeyIsNotAPrivateKey() throws Exception {
thrown.expectCause(any(InvalidKeySpecException.class));

String key = "";
objectMapper.readValue("{\"type\": \"base64\", \"key\": \"" + key + "\"}", EncodedPrivateKeyConfiguration.class);
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false).readValue("{\"key\": \"" + key + "\"}", EncodedPrivateKeyConfiguration.class);
}

@Test(expected = EncodedPrivateKeyDeserializer.PrivateKeyNotSpecifiedException.class)
public void should_throwAnExceptionWhenIncorrectFieldSpecified() throws Exception {
objectMapper.readValue("{\"type\": \"base64\", \"privateKeyFoo\": \"" + "foobar" + "\"}", EncodedPrivateKeyConfiguration.class);
objectMapper.readValue("{\"privateKeyFoo\": \"" + "foobar" + "\"}", EncodedPrivateKeyConfiguration.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ public class PrivateKeyFileConfigurationTest {
@Test
public void should_loadPrivateKeyFromJSON() throws Exception {
String path = getClass().getClassLoader().getResource("private_key.pk8").getPath();
PrivateKeyFileConfiguration privateKeyFileConfiguration = objectMapper.readValue("{\"type\": \"file\", \"keyFile\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);
PrivateKeyFileConfiguration privateKeyFileConfiguration = objectMapper.readValue("{\"keyFile\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);

assertThat(privateKeyFileConfiguration.getPrivateKey().getAlgorithm()).isEqualTo("RSA");
}

@Test(expected = FileNotFoundException.class)
public void should_ThrowFooExceptionWhenFileDoesNotExist() throws Exception {
objectMapper.readValue("{\"type\": \"file\", \"keyFile\": \"/foo/bar\"}", PrivateKeyFileConfiguration.class);
objectMapper.readValue("{\"keyFile\": \"/foo/bar\"}", PrivateKeyFileConfiguration.class);
}

@Test
Expand All @@ -38,12 +38,12 @@ public void should_ThrowFooExceptionWhenFileDoesNotContainAPrivateKey() throws E
thrown.expectCause(any(InvalidKeySpecException.class));

String path = getClass().getClassLoader().getResource("empty_file").getPath();
objectMapper.readValue("{\"type\": \"file\", \"keyFile\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);
objectMapper.readValue("{\"keyFile\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);
}

@Test(expected = PrivateKeyFileDeserializer.PrivateKeyPathNotSpecifiedException.class)
public void should_throwAnExceptionWhenIncorrectKeySpecified() throws Exception {
String path = getClass().getClassLoader().getResource("empty_file").getPath();
objectMapper.readValue("{\"type\": \"file\", \"privateKeyFoo\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);
objectMapper.readValue("{\"privateKeyFoo\": \"" + path + "\"}", PrivateKeyFileConfiguration.class);
}
}

0 comments on commit cf8d90f

Please sign in to comment.