mirror of https://github.com/shred/acme4j
Add support for sub-problems
parent
1eedc755ea
commit
d6fb218a27
|
@ -13,9 +13,16 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j;
|
package org.shredzone.acme4j;
|
||||||
|
|
||||||
|
import static java.util.Collections.unmodifiableList;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
import org.shredzone.acme4j.toolbox.JSON;
|
import org.shredzone.acme4j.toolbox.JSON;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +33,7 @@ import org.shredzone.acme4j.toolbox.JSON;
|
||||||
public class Problem implements Serializable {
|
public class Problem implements Serializable {
|
||||||
private static final long serialVersionUID = -8418248862966754214L;
|
private static final long serialVersionUID = -8418248862966754214L;
|
||||||
|
|
||||||
private final URI baseUri;
|
private final URL baseUrl;
|
||||||
private final JSON problemJson;
|
private final JSON problemJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,26 +41,34 @@ public class Problem implements Serializable {
|
||||||
*
|
*
|
||||||
* @param problem
|
* @param problem
|
||||||
* Problem as JSON structure
|
* Problem as JSON structure
|
||||||
* @param baseUri
|
* @param baseUrl
|
||||||
* Document's base {@link URI} to resolve relative URIs against
|
* Document's base {@link URL} to resolve relative URIs against
|
||||||
*/
|
*/
|
||||||
public Problem(JSON problem, URI baseUri) {
|
public Problem(JSON problem, URL baseUrl) {
|
||||||
this.problemJson = problem;
|
this.problemJson = problem;
|
||||||
this.baseUri = baseUri;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the problem type. It is always an absolute URI.
|
* Returns the problem type. It is always an absolute URI.
|
||||||
*/
|
*/
|
||||||
public URI getType() {
|
public URI getType() {
|
||||||
|
try {
|
||||||
String type = problemJson.get("type").asString();
|
String type = problemJson.get("type").asString();
|
||||||
return type != null ? baseUri.resolve(type) : null;
|
return type != null ? baseUrl.toURI().resolve(type) : null;
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException("Bad base URL", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a human-readable description of the problem.
|
* Returns a human-readable description of the problem.
|
||||||
*/
|
*/
|
||||||
public String getDetail() {
|
public String getDetail() {
|
||||||
|
String value = problemJson.get("value").asString();
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
return problemJson.get("detail").asString();
|
return problemJson.get("detail").asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +77,41 @@ public class Problem implements Serializable {
|
||||||
* an absolute URI.
|
* an absolute URI.
|
||||||
*/
|
*/
|
||||||
public URI getInstance() {
|
public URI getInstance() {
|
||||||
|
try {
|
||||||
String instance = problemJson.get("instance").asString();
|
String instance = problemJson.get("instance").asString();
|
||||||
return instance != null ? baseUri.resolve(instance) : null;
|
return instance != null ? baseUrl.toURI().resolve(instance) : null;
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException("Bad base URL", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the domain this problem relates to. May be {@code null}.
|
||||||
|
*/
|
||||||
|
public String getDomain() {
|
||||||
|
JSON identifier = problemJson.get("identifier").asObject();
|
||||||
|
if (identifier == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = identifier.get("type").asString();
|
||||||
|
if (!"dns".equals(type)) {
|
||||||
|
throw new AcmeProtocolException("Cannot process a " + type + " identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier.get("value").asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of sub-problems. May be empty, but is never {@code null}.
|
||||||
|
*/
|
||||||
|
public List<Problem> getSubProblems() {
|
||||||
|
return unmodifiableList(
|
||||||
|
problemJson.get("sub-problems")
|
||||||
|
.asArray().stream()
|
||||||
|
.map(o -> o.asProblem(baseUrl))
|
||||||
|
.collect(toList())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -238,10 +238,8 @@ public class DefaultConnection implements Connection {
|
||||||
throw new AcmeException("HTTP " + rc + ": " + conn.getResponseMessage());
|
throw new AcmeException("HTTP " + rc + ": " + conn.getResponseMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Problem problem = new Problem(readJsonResponse(), conn.getURL().toURI());
|
Problem problem = new Problem(readJsonResponse(), conn.getURL());
|
||||||
throw createAcmeException(problem);
|
throw createAcmeException(problem);
|
||||||
} catch (URISyntaxException ex) {
|
|
||||||
throw new AcmeProtocolException("Bad request URL: " + conn.getURL(), ex);
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AcmeNetworkException(ex);
|
throw new AcmeNetworkException(ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,11 +315,7 @@ public final class JSON implements Serializable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return new Problem(asObject(), baseUrl);
|
||||||
return new Problem(asObject(), baseUrl.toURI());
|
|
||||||
} catch (URISyntaxException ex) {
|
|
||||||
throw new IllegalArgumentException("Bad base URL", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,11 +13,15 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j;
|
package org.shredzone.acme4j;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.shredzone.acme4j.toolbox.TestUtils.url;
|
||||||
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
|
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.shredzone.acme4j.toolbox.JSON;
|
import org.shredzone.acme4j.toolbox.JSON;
|
||||||
|
@ -30,16 +34,31 @@ public class ProblemTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProblem() {
|
public void testProblem() {
|
||||||
URI baseUri = URI.create("https://example.com/acme/1");
|
URL baseUrl = url("https://example.com/acme/1");
|
||||||
JSON original = TestUtils.getJSON("problem");
|
JSON original = TestUtils.getJSON("problem");
|
||||||
|
|
||||||
Problem problem = new Problem(original, baseUri);
|
Problem problem = new Problem(original, baseUrl);
|
||||||
|
|
||||||
assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:connection")));
|
assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:malformed")));
|
||||||
assertThat(problem.getDetail(), is("connection refused"));
|
assertThat(problem.getDetail(), is("Some of the identifiers requested were rejected"));
|
||||||
assertThat(problem.getInstance(), is(URI.create("https://example.com/documents/error.html")));
|
assertThat(problem.getInstance(), is(URI.create("https://example.com/documents/error.html")));
|
||||||
|
assertThat(problem.getDomain(), is(nullValue()));
|
||||||
assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString())));
|
assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString())));
|
||||||
assertThat(problem.toString(), is(sameJSONAs(original.toString())));
|
assertThat(problem.toString(), is(sameJSONAs(original.toString())));
|
||||||
|
|
||||||
|
List<Problem> subs = problem.getSubProblems();
|
||||||
|
assertThat(subs, not(nullValue()));
|
||||||
|
assertThat(subs, hasSize(2));
|
||||||
|
|
||||||
|
Problem p1 = subs.get(0);
|
||||||
|
assertThat(p1.getType(), is(URI.create("urn:ietf:params:acme:error:malformed")));
|
||||||
|
assertThat(p1.getDetail(), is("Invalid underscore in DNS name \"_example.com\""));
|
||||||
|
assertThat(p1.getDomain(), is("_example.com"));
|
||||||
|
|
||||||
|
Problem p2 = subs.get(1);
|
||||||
|
assertThat(p2.getType(), is(URI.create("urn:ietf:params:acme:error:rejectedIdentifier")));
|
||||||
|
assertThat(p2.getDetail(), is("This CA will not issue for \"example.net\""));
|
||||||
|
assertThat(p2.getDomain(), is("example.net"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,8 +302,6 @@ public final class TestUtils {
|
||||||
* @return Created {@link Problem} object
|
* @return Created {@link Problem} object
|
||||||
*/
|
*/
|
||||||
public static Problem createProblem(URI type, String detail, URL instance) {
|
public static Problem createProblem(URI type, String detail, URL instance) {
|
||||||
URI baseUri = URI.create("https://example.com/acme/1");
|
|
||||||
|
|
||||||
JSONBuilder jb = new JSONBuilder();
|
JSONBuilder jb = new JSONBuilder();
|
||||||
jb.put("type", type);
|
jb.put("type", type);
|
||||||
jb.put("detail", detail);
|
jb.put("detail", detail);
|
||||||
|
@ -311,7 +309,7 @@ public final class TestUtils {
|
||||||
jb.put("instance", instance);
|
jb.put("instance", instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Problem(jb.toJSON(), baseUri);
|
return new Problem(jb.toJSON(), url("https://example.com/acme/1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
{
|
{
|
||||||
"type": "urn:ietf:params:acme:error:connection",
|
"type": "urn:ietf:params:acme:error:malformed",
|
||||||
"detail": "connection refused",
|
"detail": "Some of the identifiers requested were rejected",
|
||||||
"instance": "/documents/error.html"
|
"instance": "/documents/error.html",
|
||||||
|
"sub-problems": [
|
||||||
|
{
|
||||||
|
"type": "urn:ietf:params:acme:error:malformed",
|
||||||
|
"value": "Invalid underscore in DNS name \"_example.com\"",
|
||||||
|
"identifier": {
|
||||||
|
"type": "dns",
|
||||||
|
"value": "_example.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "urn:ietf:params:acme:error:rejectedIdentifier",
|
||||||
|
"value": "This CA will not issue for \"example.net\"",
|
||||||
|
"identifier": {
|
||||||
|
"type": "dns",
|
||||||
|
"value": "example.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue