diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeClient.java b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeClient.java index d419e07e..dd76e2cb 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeClient.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeClient.java @@ -98,6 +98,16 @@ public interface AcmeClient { */ void updateAuthorization(Authorization auth) throws AcmeException; + /** + * Deletes an {@link Authorization}. + * + * @param registration + * {@link Registration} the authorization is related to + * @param auth + * {@link Authorization} to delete + */ + void deleteAuthorization(Registration registration, Authorization auth) throws AcmeException; + /** * Triggers a {@link Challenge}. The ACME server is requested to validate the * response. Note that the validation is performed asynchronously. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/impl/AbstractAcmeClient.java b/acme4j-client/src/main/java/org/shredzone/acme4j/impl/AbstractAcmeClient.java index 51df572d..fe31dea4 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/impl/AbstractAcmeClient.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/impl/AbstractAcmeClient.java @@ -341,6 +341,33 @@ public abstract class AbstractAcmeClient implements AcmeClient { } } + @Override + public void deleteAuthorization(Registration registration, Authorization auth) throws AcmeException { + if (registration == null) { + throw new NullPointerException("registration must not be null"); + } + if (auth == null) { + throw new NullPointerException("auth must not be null"); + } + if (auth.getLocation() == null) { + throw new IllegalArgumentException("auth location must not be null. Use newAuthorization() if not known."); + } + + LOG.debug("deleteAuthorization"); + try (Connection conn = createConnection()) { + ClaimBuilder claims = new ClaimBuilder(); + claims.putResource("authz"); + claims.put("delete", true); + + int rc = conn.sendSignedRequest(auth.getLocation(), claims, session, registration); + if (rc != HttpURLConnection.HTTP_OK) { + conn.throwAcmeException(); + } + } catch (IOException ex) { + throw new AcmeNetworkException(ex); + } + } + @Override public void triggerChallenge(Registration registration, Challenge challenge) throws AcmeException { if (registration == null) { diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/impl/AbstractAcmeClientTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/impl/AbstractAcmeClientTest.java index a10c36a8..871ac952 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/impl/AbstractAcmeClientTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/impl/AbstractAcmeClientTest.java @@ -382,6 +382,30 @@ public class AbstractAcmeClientTest { (Challenge) httpChallenge, (Challenge) dnsChallenge)); } + /** + * Test that an {@link Authorization} can be deleted. + */ + @Test + public void testDeleteAuthorization() throws AcmeException { + Authorization auth = new Authorization(locationUri); + + Connection connection = new DummyConnection() { + @Override + public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session, Registration registration) { + Map claimMap = claims.toMap(); + assertThat(claimMap.get("resource"), is((Object) "authz")); + assertThat(claimMap.get("delete"), is((Object) Boolean.TRUE)); + assertThat(uri, is(locationUri)); + assertThat(session, is(notNullValue())); + assertThat(registration.getKeyPair(), is(sameInstance(accountKeyPair))); + return HttpURLConnection.HTTP_OK; + } + }; + + TestableAbstractAcmeClient client = new TestableAbstractAcmeClient(connection); + client.deleteAuthorization(testRegistration, auth); + } + /** * Test that a {@link Challenge} can be triggered. */ diff --git a/src/site/markdown/usage/authorization.md b/src/site/markdown/usage/authorization.md index 56eb111c..4c52d34d 100644 --- a/src/site/markdown/usage/authorization.md +++ b/src/site/markdown/usage/authorization.md @@ -75,6 +75,16 @@ client.updateAuthorization(auth); After that call, the `Authorization` object contains the current server state about your authorization, including the domain name, the overall status, and an expiry date. +## Delete an Authorization + +It is possible to delete an Authorization, for example if you sell the associated domain. + +```java +URI authUri = ... // Authorization URI +Authorization auth = new Authorization(authUri); +client.deleteAuthorization(registration, auth); +``` + ## Restore a Challenge Validating a challenge can take a considerable amount of time and is a candidate for asynchronous execution. This can be a problem if you need to keep the `Challenge` object for a later time or a different Java environment.