mirror of https://github.com/shred/acme4j
Use Problem in AcmeServerException
parent
c623d72426
commit
02cedf9935
|
@ -172,7 +172,7 @@ public class Order extends AcmeResource {
|
|||
URL certUrl = json.get("certificate").asURL();
|
||||
certificate = certUrl != null ? Certificate.bind(getSession(), certUrl) : null;
|
||||
|
||||
this.error = json.get("error").asProblem();
|
||||
this.error = json.get("error").asProblem(getLocation());
|
||||
|
||||
this.authorizations = json.get("authorizations").asArray().stream()
|
||||
.map(JSON.Value::asURL)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package org.shredzone.acme4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
|
||||
import org.shredzone.acme4j.util.JSON;
|
||||
|
||||
|
@ -25,6 +26,7 @@ import org.shredzone.acme4j.util.JSON;
|
|||
public class Problem implements Serializable {
|
||||
private static final long serialVersionUID = -8418248862966754214L;
|
||||
|
||||
private final URI baseUri;
|
||||
private final JSON problem;
|
||||
|
||||
/**
|
||||
|
@ -32,16 +34,20 @@ public class Problem implements Serializable {
|
|||
*
|
||||
* @param problem
|
||||
* Problem as JSON structure
|
||||
* @param baseUri
|
||||
* Document's base {@link URI} to resolve relative URIs against
|
||||
*/
|
||||
public Problem(JSON problem) {
|
||||
public Problem(JSON problem, URI baseUri) {
|
||||
this.problem = problem;
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the problem type.
|
||||
* Returns the problem type. It is always an absolute URI.
|
||||
*/
|
||||
public String getType() {
|
||||
return problem.get("type").asString();
|
||||
public URI getType() {
|
||||
String type = problem.get("type").asString();
|
||||
return type != null ? baseUri.resolve(type) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,6 +57,15 @@ public class Problem implements Serializable {
|
|||
return problem.get("detail").asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URI that identifies the specific occurence of the problem. It is always
|
||||
* an absolute URI.
|
||||
*/
|
||||
public URI getInstance() {
|
||||
String instance = problem.get("instance").asString();
|
||||
return instance != null ? baseUri.resolve(instance) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the problem as {@link JSON} object, to access other fields.
|
||||
*/
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.jose4j.base64url.Base64Url;
|
|||
import org.jose4j.jwk.PublicJsonWebKey;
|
||||
import org.jose4j.jws.JsonWebSignature;
|
||||
import org.jose4j.lang.JoseException;
|
||||
import org.shredzone.acme4j.Problem;
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.exception.AcmeException;
|
||||
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
||||
|
@ -237,7 +238,10 @@ public class DefaultConnection implements Connection {
|
|||
throw new AcmeException("HTTP " + rc + ": " + conn.getResponseMessage());
|
||||
}
|
||||
|
||||
throw createAcmeException(readJsonResponse());
|
||||
Problem problem = new Problem(readJsonResponse(), conn.getURL().toURI());
|
||||
throw createAcmeException(problem);
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new AcmeProtocolException("Bad request URI: " + conn.getURL(), ex);
|
||||
} catch (IOException ex) {
|
||||
throw new AcmeNetworkException(ex);
|
||||
}
|
||||
|
@ -411,32 +415,29 @@ public class DefaultConnection implements Connection {
|
|||
* {@link AcmeServerException} or subtype will be thrown. Otherwise a generic
|
||||
* {@link AcmeException} is thrown.
|
||||
*/
|
||||
private AcmeException createAcmeException(JSON json) {
|
||||
String type = json.get("type").asString();
|
||||
String detail = json.get("detail").asString();
|
||||
String error = AcmeUtils.stripErrorPrefix(type);
|
||||
|
||||
if (type == null) {
|
||||
return new AcmeException(detail);
|
||||
private AcmeException createAcmeException(Problem problem) {
|
||||
if (problem.getType() == null) {
|
||||
return new AcmeException(problem.getDetail());
|
||||
}
|
||||
|
||||
String error = AcmeUtils.stripErrorPrefix(problem.getType().toString());
|
||||
|
||||
if ("unauthorized".equals(error)) {
|
||||
return new AcmeUnauthorizedException(type, detail);
|
||||
return new AcmeUnauthorizedException(problem);
|
||||
}
|
||||
|
||||
if ("userActionRequired".equals(error)) {
|
||||
URI instance = resolveRelative(json.get("instance").asString());
|
||||
URI tos = getLinks("terms-of-service").stream().findFirst().orElse(null);
|
||||
return new AcmeUserActionRequiredException(type, detail, tos, toURL(instance));
|
||||
return new AcmeUserActionRequiredException(problem, tos);
|
||||
}
|
||||
|
||||
if ("rateLimited".equals(error)) {
|
||||
Optional<Instant> retryAfter = getRetryAfterHeader();
|
||||
Collection<URI> rateLimits = getLinks("urn:ietf:params:acme:documentation");
|
||||
return new AcmeRateLimitExceededException(type, detail, retryAfter.orElse(null), rateLimits);
|
||||
return new AcmeRateLimitExceededException(problem, retryAfter.orElse(null), rateLimits);
|
||||
}
|
||||
|
||||
return new AcmeServerException(type, detail);
|
||||
return new AcmeServerException(problem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,8 @@ import java.time.Instant;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* An exception that is thrown when a rate limit was exceeded.
|
||||
*/
|
||||
|
@ -30,19 +32,16 @@ public class AcmeRateLimitExceededException extends AcmeServerException {
|
|||
/**
|
||||
* Creates a new {@link AcmeRateLimitExceededException}.
|
||||
*
|
||||
* @param type
|
||||
* System readable error type (here
|
||||
* {@code "urn:ietf:params:acme:error:rateLimited"})
|
||||
* @param detail
|
||||
* Human readable error message
|
||||
* @param problem
|
||||
* {@link Problem} that caused the exception
|
||||
* @param retryAfter
|
||||
* The moment the request is expected to succeed again, may be {@code null}
|
||||
* if not known
|
||||
* @param documents
|
||||
* URIs pointing to documents about the rate limit that was hit
|
||||
*/
|
||||
public AcmeRateLimitExceededException(String type, String detail, Instant retryAfter, Collection<URI> documents) {
|
||||
super(type, detail);
|
||||
public AcmeRateLimitExceededException(Problem problem, Instant retryAfter, Collection<URI> documents) {
|
||||
super(problem);
|
||||
this.retryAfter = retryAfter;
|
||||
this.documents =
|
||||
documents != null ? Collections.unmodifiableCollection(documents) : null;
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
*/
|
||||
package org.shredzone.acme4j.exception;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* An exception that is thrown when the ACME server returned an error. It contains
|
||||
* further details of the cause.
|
||||
|
@ -22,27 +25,31 @@ import java.util.Objects;
|
|||
public class AcmeServerException extends AcmeException {
|
||||
private static final long serialVersionUID = 5971622508467042792L;
|
||||
|
||||
private final String type;
|
||||
private final Problem problem;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AcmeServerException}.
|
||||
*
|
||||
* @param type
|
||||
* System readable error type (e.g.
|
||||
* {@code "urn:ietf:params:acme:error:malformed"})
|
||||
* @param detail
|
||||
* Human readable error message
|
||||
* @param problem
|
||||
* {@link Problem} that caused the exception
|
||||
*/
|
||||
public AcmeServerException(String type, String detail) {
|
||||
super(detail);
|
||||
this.type = Objects.requireNonNull(type, "type");
|
||||
public AcmeServerException(Problem problem) {
|
||||
super(Objects.requireNonNull(problem).getDetail());
|
||||
this.problem = problem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error type.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
public URI getType() {
|
||||
return problem.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Problem} that caused the exception
|
||||
*/
|
||||
public Problem getProblem() {
|
||||
return problem;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
*/
|
||||
package org.shredzone.acme4j.exception;
|
||||
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* An exception that is thrown when the client is not authorized. The details will give
|
||||
* an explanation for the reasons (e.g. "client not on a whitelist").
|
||||
|
@ -23,14 +25,11 @@ public class AcmeUnauthorizedException extends AcmeServerException {
|
|||
/**
|
||||
* Creates a new {@link AcmeUnauthorizedException}.
|
||||
*
|
||||
* @param type
|
||||
* System readable error type (here
|
||||
* {@code "urn:ietf:params:acme:error:unauthorized"})
|
||||
* @param detail
|
||||
* Human readable error message
|
||||
* @param problem
|
||||
* {@link Problem} that caused the exception
|
||||
*/
|
||||
public AcmeUnauthorizedException(String type, String detail) {
|
||||
super(type, detail);
|
||||
public AcmeUnauthorizedException(Problem problem) {
|
||||
super(problem);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
*/
|
||||
package org.shredzone.acme4j.exception;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* An exception that is thrown when the user is required to take action as indicated.
|
||||
*/
|
||||
|
@ -23,26 +26,18 @@ public class AcmeUserActionRequiredException extends AcmeServerException {
|
|||
private static final long serialVersionUID = 7719055447283858352L;
|
||||
|
||||
private final URI tosUri;
|
||||
private final URL instance;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AcmeUserActionRequiredException}.
|
||||
*
|
||||
* @param type
|
||||
* System readable error type (here
|
||||
* {@code "urn:ietf:params:acme:error:userActionRequired"})
|
||||
* @param detail
|
||||
* Human readable error message
|
||||
* @param problem
|
||||
* {@link Problem} that caused the exception
|
||||
* @param tosUri
|
||||
* {@link URI} of the terms-of-service document to accept
|
||||
* @param instance
|
||||
* {@link URL} to be visited by a human, showing instructions for how to
|
||||
* agree to the terms and conditions.
|
||||
*/
|
||||
public AcmeUserActionRequiredException(String type, String detail, URI tosUri, URL instance) {
|
||||
super(type, detail);
|
||||
public AcmeUserActionRequiredException(Problem problem, URI tosUri) {
|
||||
super(problem);
|
||||
this.tosUri = tosUri;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +53,13 @@ public class AcmeUserActionRequiredException extends AcmeServerException {
|
|||
* or {@code null} if the server did not provide such a link.
|
||||
*/
|
||||
public URL getInstance() {
|
||||
return instance;
|
||||
try {
|
||||
URI instance = getProblem().getInstance();
|
||||
return instance != null ? instance.toURL() : null;
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new AcmeProtocolException(
|
||||
"Bad instance URL: " + getProblem().getInstance().toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -306,14 +306,20 @@ public final class JSON implements Serializable {
|
|||
/**
|
||||
* Returns the value as {@link Problem}.
|
||||
*
|
||||
* @param baseUrl
|
||||
* Base {@link URL} to resolve relative links against
|
||||
* @return {@link Problem}, or {@code null} if the value was not set.
|
||||
*/
|
||||
public Problem asProblem() {
|
||||
public Problem asProblem(URL baseUrl) {
|
||||
if (val == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Problem(asObject());
|
||||
try {
|
||||
return new Problem(asObject(), baseUrl.toURI());
|
||||
} catch (URISyntaxException ex) {
|
||||
throw new IllegalArgumentException("Bad base URL", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp;
|
|||
import static org.shredzone.acme4j.util.TestUtils.*;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
@ -76,7 +77,7 @@ public class OrderTest {
|
|||
assertThat(order.getCsr(), is(csr));
|
||||
|
||||
assertThat(order.getError(), is(notNullValue()));
|
||||
assertThat(order.getError().getType(), is("urn:ietf:params:acme:error:connection"));
|
||||
assertThat(order.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection")));
|
||||
assertThat(order.getError().getDetail(), is("connection refused"));
|
||||
|
||||
List<Authorization> auths = order.getAuthorizations();
|
||||
|
|
|
@ -17,6 +17,8 @@ import static org.hamcrest.Matchers.is;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.shredzone.acme4j.util.JSON;
|
||||
import org.shredzone.acme4j.util.TestUtils;
|
||||
|
@ -28,12 +30,14 @@ public class ProblemTest {
|
|||
|
||||
@Test
|
||||
public void testProblem() {
|
||||
URI baseUri = URI.create("https://example.com/acme/1");
|
||||
JSON original = TestUtils.getJSON("problem");
|
||||
|
||||
Problem problem = new Problem(original);
|
||||
Problem problem = new Problem(original, baseUri);
|
||||
|
||||
assertThat(problem.getType(), is("urn:ietf:params:acme:error:connection"));
|
||||
assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:connection")));
|
||||
assertThat(problem.getDetail(), is("connection refused"));
|
||||
assertThat(problem.getInstance(), is(URI.create("https://example.com/documents/error.html")));
|
||||
assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString())));
|
||||
assertThat(problem.toString(), is(sameJSONAs(original.toString())));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.shredzone.acme4j.connector;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.shredzone.acme4j.util.TestUtils.url;
|
||||
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -425,13 +426,14 @@ public class DefaultConnectionTest {
|
|||
when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/problem+json");
|
||||
when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN);
|
||||
when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
|
||||
when(mockUrlConnection.getURL()).thenReturn(url("https://example.com/acme/1"));
|
||||
|
||||
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
|
||||
conn.conn = mockUrlConnection;
|
||||
conn.accept(HttpURLConnection.HTTP_OK);
|
||||
fail("Expected to fail");
|
||||
} catch (AcmeServerException ex) {
|
||||
assertThat(ex.getType(), is("urn:ietf:params:acme:error:unauthorized"));
|
||||
assertThat(ex.getType(), is(URI.create("urn:ietf:params:acme:error:unauthorized")));
|
||||
assertThat(ex.getMessage(), is("Invalid response: 404"));
|
||||
} catch (AcmeException ex) {
|
||||
fail("Expected an AcmeServerException");
|
||||
|
@ -440,6 +442,7 @@ public class DefaultConnectionTest {
|
|||
verify(mockUrlConnection, atLeastOnce()).getHeaderField("Content-Type");
|
||||
verify(mockUrlConnection, atLeastOnce()).getResponseCode();
|
||||
verify(mockUrlConnection).getErrorStream();
|
||||
verify(mockUrlConnection).getURL();
|
||||
verifyNoMoreInteractions(mockUrlConnection);
|
||||
}
|
||||
|
||||
|
@ -452,6 +455,8 @@ public class DefaultConnectionTest {
|
|||
.thenReturn("application/problem+json");
|
||||
when(mockUrlConnection.getResponseCode())
|
||||
.thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR);
|
||||
when(mockUrlConnection.getURL())
|
||||
.thenReturn(url("https://example.com/acme/1"));
|
||||
|
||||
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
|
||||
@Override
|
||||
|
@ -466,7 +471,7 @@ public class DefaultConnectionTest {
|
|||
conn.accept(HttpURLConnection.HTTP_OK);
|
||||
fail("Expected to fail");
|
||||
} catch (AcmeServerException ex) {
|
||||
assertThat(ex.getType(), is("urn:zombie:error:apocalypse"));
|
||||
assertThat(ex.getType(), is(URI.create("urn:zombie:error:apocalypse")));
|
||||
assertThat(ex.getMessage(), is("Zombie apocalypse in progress"));
|
||||
} catch (AcmeException ex) {
|
||||
fail("Expected an AcmeServerException");
|
||||
|
@ -474,6 +479,7 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(mockUrlConnection).getHeaderField("Content-Type");
|
||||
verify(mockUrlConnection, atLeastOnce()).getResponseCode();
|
||||
verify(mockUrlConnection).getURL();
|
||||
verifyNoMoreInteractions(mockUrlConnection);
|
||||
}
|
||||
|
||||
|
@ -486,6 +492,8 @@ public class DefaultConnectionTest {
|
|||
.thenReturn("application/problem+json");
|
||||
when(mockUrlConnection.getResponseCode())
|
||||
.thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR);
|
||||
when(mockUrlConnection.getURL())
|
||||
.thenReturn(url("https://example.com/acme/1"));
|
||||
|
||||
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
|
||||
@Override
|
||||
|
@ -504,6 +512,7 @@ public class DefaultConnectionTest {
|
|||
|
||||
verify(mockUrlConnection).getHeaderField("Content-Type");
|
||||
verify(mockUrlConnection, atLeastOnce()).getResponseCode();
|
||||
verify(mockUrlConnection).getURL();
|
||||
verifyNoMoreInteractions(mockUrlConnection);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.shredzone.acme4j.exception;
|
|||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.shredzone.acme4j.util.TestUtils.createProblem;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
@ -23,6 +24,7 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AcmeRateLimitExceededException}.
|
||||
|
@ -34,15 +36,17 @@ public class AcmeRateLimitExceededExceptionTest {
|
|||
*/
|
||||
@Test
|
||||
public void testAcmeRateLimitExceededException() {
|
||||
String type = "urn:ietf:params:acme:error:rateLimited";
|
||||
URI type = URI.create("urn:ietf:params:acme:error:rateLimited");
|
||||
String detail = "Too many requests per minute";
|
||||
Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1));
|
||||
Collection<URI> documents = Arrays.asList(
|
||||
URI.create("http://example.com/doc1.html"),
|
||||
URI.create("http://example.com/doc2.html"));
|
||||
|
||||
Problem problem = createProblem(type, detail, null);
|
||||
|
||||
AcmeRateLimitExceededException ex
|
||||
= new AcmeRateLimitExceededException(type, detail, retryAfter, documents);
|
||||
= new AcmeRateLimitExceededException(problem, retryAfter, documents);
|
||||
|
||||
assertThat(ex.getType(), is(type));
|
||||
assertThat(ex.getMessage(), is(detail));
|
||||
|
@ -55,11 +59,13 @@ public class AcmeRateLimitExceededExceptionTest {
|
|||
*/
|
||||
@Test
|
||||
public void testNullAcmeRateLimitExceededException() {
|
||||
String type = "urn:ietf:params:acme:error:rateLimited";
|
||||
URI type = URI.create("urn:ietf:params:acme:error:rateLimited");
|
||||
String detail = "Too many requests per minute";
|
||||
|
||||
Problem problem = createProblem(type, detail, null);
|
||||
|
||||
AcmeRateLimitExceededException ex
|
||||
= new AcmeRateLimitExceededException(type, detail, null, null);
|
||||
= new AcmeRateLimitExceededException(problem, null, null);
|
||||
|
||||
assertThat(ex.getType(), is(type));
|
||||
assertThat(ex.getMessage(), is(detail));
|
||||
|
|
|
@ -15,12 +15,14 @@ package org.shredzone.acme4j.exception;
|
|||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.shredzone.acme4j.util.TestUtils.createProblem;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.shredzone.acme4j.Problem;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AcmeUserActionRequiredException}.
|
||||
|
@ -32,13 +34,15 @@ public class AcmeUserActionRequiredExceptionTest {
|
|||
*/
|
||||
@Test
|
||||
public void testAcmeUserActionRequiredException() throws MalformedURLException {
|
||||
String type = "urn:ietf:params:acme:error:userActionRequired";
|
||||
URI type = URI.create("urn:ietf:params:acme:error:userActionRequired");
|
||||
String detail = "Accept new TOS";
|
||||
URI tosUri = URI.create("http://example.com/agreement.pdf");
|
||||
URL instanceUrl = new URL("http://example.com/howToAgree.html");
|
||||
|
||||
Problem problem = createProblem(type, detail, instanceUrl);
|
||||
|
||||
AcmeUserActionRequiredException ex
|
||||
= new AcmeUserActionRequiredException(type, detail, tosUri, instanceUrl);
|
||||
= new AcmeUserActionRequiredException(problem, tosUri);
|
||||
|
||||
assertThat(ex.getType(), is(type));
|
||||
assertThat(ex.getMessage(), is(detail));
|
||||
|
@ -51,11 +55,13 @@ public class AcmeUserActionRequiredExceptionTest {
|
|||
*/
|
||||
@Test
|
||||
public void testNullAcmeUserActionRequiredException() {
|
||||
String type = "urn:ietf:params:acme:error:userActionRequired";
|
||||
URI type = URI.create("urn:ietf:params:acme:error:userActionRequired");
|
||||
String detail = "Call our service";
|
||||
|
||||
Problem problem = createProblem(type, detail, null);
|
||||
|
||||
AcmeUserActionRequiredException ex
|
||||
= new AcmeUserActionRequiredException(type, detail, null, null);
|
||||
= new AcmeUserActionRequiredException(problem, null);
|
||||
|
||||
assertThat(ex.getType(), is(type));
|
||||
assertThat(ex.getMessage(), is(detail));
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.io.ObjectInputStream;
|
|||
import java.io.ObjectOutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
|
@ -45,6 +46,8 @@ import org.shredzone.acme4j.exception.AcmeProtocolException;
|
|||
*/
|
||||
public class JSONTest {
|
||||
|
||||
private static final URL BASE_URL = url("https://example.com/acme/1");
|
||||
|
||||
/**
|
||||
* Test that an empty {@link JSON} is empty.
|
||||
*/
|
||||
|
@ -210,10 +213,11 @@ public class JSONTest {
|
|||
JSON sub = array.get(3).asObject();
|
||||
assertThat(sub.get("test").asString(), is("ok"));
|
||||
|
||||
Problem problem = json.get("problem").asProblem();
|
||||
Problem problem = json.get("problem").asProblem(BASE_URL);
|
||||
assertThat(problem, is(notNullValue()));
|
||||
assertThat(problem.getType(), is("urn:ietf:params:acme:error:rateLimited"));
|
||||
assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:rateLimited")));
|
||||
assertThat(problem.getDetail(), is("too many requests"));
|
||||
assertThat(problem.getInstance(), is(URI.create("https://example.com/documents/errors.html")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -231,7 +235,7 @@ public class JSONTest {
|
|||
assertThat(json.get("none").asObject(), is(nullValue()));
|
||||
assertThat(json.get("none").asStatusOrElse(Status.INVALID), is(Status.INVALID));
|
||||
assertThat(json.get("none").asBinary(), is(nullValue()));
|
||||
assertThat(json.get("none").asProblem(), is(nullValue()));
|
||||
assertThat(json.get("none").asProblem(BASE_URL), is(nullValue()));
|
||||
|
||||
try {
|
||||
json.get("none").asInt();
|
||||
|
@ -308,7 +312,7 @@ public class JSONTest {
|
|||
}
|
||||
|
||||
try {
|
||||
json.get("text").asProblem();
|
||||
json.get("text").asProblem(BASE_URL);
|
||||
fail("no exception was thrown");
|
||||
} catch (AcmeProtocolException ex) {
|
||||
// expected
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.jose4j.base64url.Base64Url;
|
|||
import org.jose4j.json.JsonUtil;
|
||||
import org.jose4j.jwk.JsonWebKey;
|
||||
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
|
||||
import org.shredzone.acme4j.Problem;
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.provider.AcmeProvider;
|
||||
|
||||
|
@ -268,6 +269,30 @@ public final class TestUtils {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Problem} with the given type and details.
|
||||
*
|
||||
* @param type
|
||||
* Problem type
|
||||
* @param detail
|
||||
* Problem details
|
||||
* @param instance
|
||||
* Instance, or {@code null}
|
||||
* @return Created {@link Problem} object
|
||||
*/
|
||||
public static Problem createProblem(URI type, String detail, URL instance) {
|
||||
URI baseUri = URI.create("https://example.com/acme/1");
|
||||
|
||||
JSONBuilder jb = new JSONBuilder();
|
||||
jb.put("type", type);
|
||||
jb.put("detail", detail);
|
||||
if (instance != null) {
|
||||
jb.put("instance", instance);
|
||||
}
|
||||
|
||||
return new Problem(jb.toJSON(), baseUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new keypair for unit tests, and return its N, E, KTY and THUMBPRINT
|
||||
* parameters to be set in the {@link TestUtils} class.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"binary": "Q2hhaW5zYXc",
|
||||
"problem": {
|
||||
"type": "urn:ietf:params:acme:error:rateLimited",
|
||||
"detail": "too many requests"
|
||||
"detail": "too many requests",
|
||||
"instance": "/documents/errors.html"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"type": "urn:ietf:params:acme:error:connection",
|
||||
"detail": "connection refused"
|
||||
"detail": "connection refused",
|
||||
"instance": "/documents/error.html"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue