mirror of https://github.com/shred/acme4j
Add method to restore a Challenge
parent
c97392236d
commit
db927300e9
|
@ -80,6 +80,19 @@ public interface AcmeClient {
|
||||||
*/
|
*/
|
||||||
void updateChallenge(Account account, Challenge challenge) throws AcmeException;
|
void updateChallenge(Account account, Challenge challenge) throws AcmeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore a {@link Challenge} instance if only the challenge URI is known. It
|
||||||
|
* contains the current state.
|
||||||
|
*
|
||||||
|
* @param account
|
||||||
|
* {@link Account} to be used for conversation
|
||||||
|
* @param challengeUri
|
||||||
|
* {@link URI} of the challenge to restore
|
||||||
|
* @throws ClassCastException
|
||||||
|
* if the challenge does not match the desired type
|
||||||
|
*/
|
||||||
|
<T extends Challenge> T restoreChallenge(Account account, URI challengeUri) throws AcmeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request a certificate.
|
* Request a certificate.
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,9 +39,9 @@ public interface Challenge {
|
||||||
String getType();
|
String getType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link URI} of the challenge.
|
* Returns the location {@link URI} of the challenge.
|
||||||
*/
|
*/
|
||||||
URI getUri();
|
URI getLocation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current status of the challenge.
|
* Returns the current status of the challenge.
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class GenericChallenge implements Challenge {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getUri() {
|
public URI getLocation() {
|
||||||
String uri = get(KEY_URI);
|
String uri = get(KEY_URI);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -202,7 +202,7 @@ public abstract class AbstractAcmeClient implements AcmeClient {
|
||||||
claims.putResource("challenge");
|
claims.putResource("challenge");
|
||||||
challenge.marshall(claims);
|
challenge.marshall(claims);
|
||||||
|
|
||||||
int rc = conn.sendSignedRequest(challenge.getUri(), claims, session, account);
|
int rc = conn.sendSignedRequest(challenge.getLocation(), claims, session, account);
|
||||||
if (rc != HttpURLConnection.HTTP_OK && rc != HttpURLConnection.HTTP_ACCEPTED) {
|
if (rc != HttpURLConnection.HTTP_OK && rc != HttpURLConnection.HTTP_ACCEPTED) {
|
||||||
conn.throwAcmeException();
|
conn.throwAcmeException();
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ public abstract class AbstractAcmeClient implements AcmeClient {
|
||||||
public void updateChallenge(Account account, Challenge challenge) throws AcmeException {
|
public void updateChallenge(Account account, Challenge challenge) throws AcmeException {
|
||||||
LOG.debug("updateChallenge");
|
LOG.debug("updateChallenge");
|
||||||
try (Connection conn = createConnection()) {
|
try (Connection conn = createConnection()) {
|
||||||
int rc = conn.sendRequest(challenge.getUri());
|
int rc = conn.sendRequest(challenge.getLocation());
|
||||||
if (rc != HttpURLConnection.HTTP_ACCEPTED) {
|
if (rc != HttpURLConnection.HTTP_ACCEPTED) {
|
||||||
conn.throwAcmeException();
|
conn.throwAcmeException();
|
||||||
}
|
}
|
||||||
|
@ -224,6 +224,27 @@ public abstract class AbstractAcmeClient implements AcmeClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Challenge> T restoreChallenge(Account account, URI challengeUri) throws AcmeException {
|
||||||
|
LOG.debug("restoreChallenge");
|
||||||
|
try (Connection conn = createConnection()) {
|
||||||
|
int rc = conn.sendRequest(challengeUri);
|
||||||
|
if (rc != HttpURLConnection.HTTP_ACCEPTED) {
|
||||||
|
conn.throwAcmeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> json = conn.readJsonResponse();
|
||||||
|
if (!(json.containsKey("type"))) {
|
||||||
|
throw new AcmeException("Provided URI is not a challenge URI");
|
||||||
|
}
|
||||||
|
|
||||||
|
T challenge = (T) createChallenge(json.get("type").toString());
|
||||||
|
challenge.unmarshall(json);
|
||||||
|
return challenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI requestCertificate(Account account, byte[] csr) throws AcmeException {
|
public URI requestCertificate(Account account, byte[] csr) throws AcmeException {
|
||||||
LOG.debug("requestCertificate");
|
LOG.debug("requestCertificate");
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class GenericChallengeTest {
|
||||||
// Test default values
|
// Test default values
|
||||||
assertThat(challenge.getType(), is(nullValue()));
|
assertThat(challenge.getType(), is(nullValue()));
|
||||||
assertThat(challenge.getStatus(), is(Status.PENDING));
|
assertThat(challenge.getStatus(), is(Status.PENDING));
|
||||||
assertThat(challenge.getUri(), is(nullValue()));
|
assertThat(challenge.getLocation(), is(nullValue()));
|
||||||
assertThat(challenge.getValidated(), is(nullValue()));
|
assertThat(challenge.getValidated(), is(nullValue()));
|
||||||
|
|
||||||
// Unmarshall a challenge JSON
|
// Unmarshall a challenge JSON
|
||||||
|
@ -58,7 +58,7 @@ public class GenericChallengeTest {
|
||||||
// Test unmarshalled values
|
// Test unmarshalled values
|
||||||
assertThat(challenge.getType(), is("generic-01"));
|
assertThat(challenge.getType(), is("generic-01"));
|
||||||
assertThat(challenge.getStatus(), is(Status.VALID));
|
assertThat(challenge.getStatus(), is(Status.VALID));
|
||||||
assertThat(challenge.getUri(), is(new URI("http://example.com/challenge/123")));
|
assertThat(challenge.getLocation(), is(new URI("http://example.com/challenge/123")));
|
||||||
assertThat(challenge.getValidated(), is("2015-12-12T17:19:36.336785823Z"));
|
assertThat(challenge.getValidated(), is("2015-12-12T17:19:36.336785823Z"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ public class AbstractAcmeClientTest {
|
||||||
client.triggerChallenge(testAccount, challenge);
|
client.triggerChallenge(testAccount, challenge);
|
||||||
|
|
||||||
assertThat(challenge.getStatus(), is(Status.PENDING));
|
assertThat(challenge.getStatus(), is(Status.PENDING));
|
||||||
assertThat(challenge.getUri(), is(locationUri));
|
assertThat(challenge.getLocation(), is(locationUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,7 +253,31 @@ public class AbstractAcmeClientTest {
|
||||||
client.updateChallenge(testAccount, challenge);
|
client.updateChallenge(testAccount, challenge);
|
||||||
|
|
||||||
assertThat(challenge.getStatus(), is(Status.VALID));
|
assertThat(challenge.getStatus(), is(Status.VALID));
|
||||||
assertThat(challenge.getUri(), is(locationUri));
|
assertThat(challenge.getLocation(), is(locationUri));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestoreChallenge() throws AcmeException {
|
||||||
|
Connection connection = new DummyConnection() {
|
||||||
|
@Override
|
||||||
|
public int sendRequest(URI uri) throws AcmeException {
|
||||||
|
assertThat(uri, is(locationUri));
|
||||||
|
return HttpURLConnection.HTTP_ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> readJsonResponse() throws AcmeException {
|
||||||
|
return getJsonAsMap("updateHttpChallengeResponse");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestableAbstractAcmeClient client = new TestableAbstractAcmeClient(connection);
|
||||||
|
client.putTestChallenge(HttpChallenge.TYPE, new HttpChallenge());
|
||||||
|
|
||||||
|
Challenge challenge = client.restoreChallenge(testAccount, locationUri);
|
||||||
|
|
||||||
|
assertThat(challenge.getStatus(), is(Status.VALID));
|
||||||
|
assertThat(challenge.getLocation(), is(locationUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,3 +62,23 @@ As soon as the challenge is `VALID`, you have successfully associated the domain
|
||||||
If your final certificate contains further domains or subdomains, repeat the authorization run with each of them.
|
If your final certificate contains further domains or subdomains, repeat the authorization run with each of them.
|
||||||
|
|
||||||
Note that wildcard certificates are not currently supported.
|
Note that wildcard certificates are not currently supported.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
To recreate a `Challenge` object at a later time, all you need is to store the original object's `location` property:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Challenge originalChallenge = ... // some Challenge instance
|
||||||
|
URI challengeUri = originalChallenge.getLocation();
|
||||||
|
```
|
||||||
|
|
||||||
|
Later, you pass this `challengeUri` to `recreateChallenge()`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
URI challengeUri = ... // challenge URI
|
||||||
|
Challenge restoredChallenge = client.restoreChallenge(account, challengeUri);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `restoredChallenge` already reflects the current state of the challenge.
|
||||||
|
|
Loading…
Reference in New Issue