Support acme-star-09 draft, change API and documentation

pull/89/head
Richard Körber 2020-01-23 23:26:48 +01:00
parent f1e3048dd2
commit 9d62cb6a55
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
16 changed files with 252 additions and 204 deletions

View File

@ -19,6 +19,7 @@ import java.net.URI;
import java.net.URL; import java.net.URL;
import java.time.Duration; import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.Optional;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
@ -88,8 +89,8 @@ public class Metadata {
* *
* @since 2.3 * @since 2.3
*/ */
public boolean isStarEnabled() { public boolean isAutoRenewalEnabled() {
return meta.get("star-enabled").map(Value::asBoolean).orElse(false); return meta.get("auto-renewal").isPresent();
} }
/** /**
@ -99,18 +100,26 @@ public class Metadata {
* *
* @since 2.3 * @since 2.3
*/ */
public Duration getStarMinCertValidity() { public Duration getAutoRenewalMinLifetime() {
return meta.get("star-min-cert-validity").map(Value::asDuration).orElse(null); Optional<JSON> ar = meta.get("auto-renewal").optional().map(Value::asObject);
if (!ar.isPresent()) {
return null;
}
return ar.get().get("min-lifetime").map(Value::asDuration).orElse(null);
} }
/** /**
* Returns the maximum delta between recurrent end date and recurrent start date. * Returns the maximum delta between auto-renewal end date and auto-renewal start
* {@code null} if the CA does not support short-term auto renewal. * date. {@code null} if the CA does not support short-term auto renewal.
* *
* @since 2.3 * @since 2.3
*/ */
public Duration getStarMaxRenewal() { public Duration getAutoRenewalMaxDuration() {
return meta.get("star-max-renewal").map(Value::asDuration).orElse(null); Optional<JSON> ar = meta.get("auto-renewal").optional().map(Value::asObject);
if (!ar.isPresent()) {
return null;
}
return ar.get().get("max-duration").map(Value::asDuration).orElse(null);
} }
/** /**
@ -118,8 +127,12 @@ public class Metadata {
* *
* @since 2.6 * @since 2.6
*/ */
public boolean isStarCertificateGetAllowed() { public boolean isAutoRenewalGetAllowed() {
return meta.get("star-allow-certificate-get").map(Value::asBoolean).orElse(false); Optional<JSON> ar = meta.get("auto-renewal").optional().map(Value::asObject);
if (!ar.isPresent()) {
return false;
}
return ar.get().get("allow-certificate-get").map(Value::asBoolean).orElse(false);
} }
/** /**

View File

@ -26,6 +26,7 @@ import javax.annotation.ParametersAreNonnullByDefault;
import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value; import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder; import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -138,7 +139,7 @@ public class Order extends AcmeJsonResource {
* @since 2.6 * @since 2.6
*/ */
@CheckForNull @CheckForNull
public Certificate getStarCertificate() { public Certificate getAutoRenewalCertificate() {
return getJSON().get("star-certificate") return getJSON().get("star-certificate")
.map(Value::asURL) .map(Value::asURL)
.map(getLogin()::bindCertificate) .map(getLogin()::bindCertificate)
@ -171,15 +172,14 @@ public class Order extends AcmeJsonResource {
} }
/** /**
* Checks if this order is recurrent, according to the ACME STAR specifications. * Checks if this order is auto-renewing, according to the ACME STAR specifications.
* *
* @since 2.3 * @since 2.3
*/ */
public boolean isRecurrent() { public boolean isAutoRenewing() {
return getJSON().get("recurrent") return getJSON().get("auto-renewal")
.optional() .optional()
.map(Value::asBoolean) .isPresent();
.orElse(false);
} }
/** /**
@ -189,8 +189,12 @@ public class Order extends AcmeJsonResource {
* @since 2.3 * @since 2.3
*/ */
@CheckForNull @CheckForNull
public Instant getRecurrentStart() { public Instant getAutoRenewalStartDate() {
return getJSON().get("recurrent-start-date") return getJSON().get("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("start-date")
.optional() .optional()
.map(Value::asInstant) .map(Value::asInstant)
.orElse(null); .orElse(null);
@ -203,21 +207,29 @@ public class Order extends AcmeJsonResource {
* @since 2.3 * @since 2.3
*/ */
@CheckForNull @CheckForNull
public Instant getRecurrentEnd() { public Instant getAutoRenewalEndDate() {
return getJSON().get("recurrent-end-date") return getJSON().get("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("end-date")
.optional() .optional()
.map(Value::asInstant) .map(Value::asInstant)
.orElse(null); .orElse(null);
} }
/** /**
* Returns the maximum validity period of each certificate, or {@code null}. * Returns the maximum lifetime of each certificate, or {@code null}.
* *
* @since 2.3 * @since 2.3
*/ */
@CheckForNull @CheckForNull
public Duration getRecurrentCertificateValidity() { public Duration getAutoRenewalLifetime() {
return getJSON().get("recurrent-certificate-validity") return getJSON().get("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("lifetime")
.optional() .optional()
.map(Value::asDuration) .map(Value::asDuration)
.orElse(null); .orElse(null);
@ -229,8 +241,12 @@ public class Order extends AcmeJsonResource {
* @since 2.7 * @since 2.7
*/ */
@CheckForNull @CheckForNull
public Duration getRecurrentCertificatePredate() { public Duration getAutoRenewalLifetimeAdjust() {
return getJSON().get("recurrent-certificate-predate") return getJSON().get("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("lifetime-adjust")
.optional() .optional()
.map(Value::asDuration) .map(Value::asDuration)
.orElse(null); .orElse(null);
@ -242,20 +258,24 @@ public class Order extends AcmeJsonResource {
* *
* @since 2.6 * @since 2.6
*/ */
public boolean isRecurrentGetEnabled() { public boolean isAutoRenewalGetEnabled() {
return getJSON().get("recurrent-certificate-get") return getJSON().get("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("allow-certificate-get")
.optional() .optional()
.map(Value::asBoolean) .map(Value::asBoolean)
.orElse(false); .orElse(false);
} }
/** /**
* Cancels a recurrent order. * Cancels an auto-renewing order.
* *
* @since 2.3 * @since 2.3
*/ */
public void cancelRecurrent() throws AcmeException { public void cancelAutoRenewal() throws AcmeException {
if (!getSession().getMetadata().isStarEnabled()) { if (!getSession().getMetadata().isAutoRenewalEnabled()) {
throw new AcmeException("CA does not support short-term automatic renewals"); throw new AcmeException("CA does not support short-term automatic renewals");
} }

View File

@ -45,12 +45,12 @@ public class OrderBuilder {
private final Set<Identifier> identifierSet = new LinkedHashSet<>(); private final Set<Identifier> identifierSet = new LinkedHashSet<>();
private Instant notBefore; private Instant notBefore;
private Instant notAfter; private Instant notAfter;
private boolean recurrent; private boolean autoRenewal;
private Instant recurrentStart; private Instant autoRenewalStart;
private Instant recurrentEnd; private Instant autoRenewalEnd;
private Duration recurrentValidity; private Duration autoRenewalLifetime;
private Duration recurrentPredate; private Duration autoRenewalLifetimeAdjust;
private boolean recurrentGet; private boolean autoRenewalGet;
/** /**
* Create a new {@link OrderBuilder}. * Create a new {@link OrderBuilder}.
@ -137,8 +137,8 @@ public class OrderBuilder {
* @return itself * @return itself
*/ */
public OrderBuilder notBefore(Instant notBefore) { public OrderBuilder notBefore(Instant notBefore) {
if (recurrent) { if (autoRenewal) {
throw new IllegalArgumentException("cannot combine notBefore with recurrent"); throw new IllegalArgumentException("cannot combine notBefore with autoRenew");
} }
this.notBefore = requireNonNull(notBefore, "notBefore"); this.notBefore = requireNonNull(notBefore, "notBefore");
return this; return this;
@ -151,8 +151,8 @@ public class OrderBuilder {
* @return itself * @return itself
*/ */
public OrderBuilder notAfter(Instant notAfter) { public OrderBuilder notAfter(Instant notAfter) {
if (recurrent) { if (autoRenewal) {
throw new IllegalArgumentException("cannot combine notAfter with recurrent"); throw new IllegalArgumentException("cannot combine notAfter with autoRenew");
} }
this.notAfter = requireNonNull(notAfter, "notAfter"); this.notAfter = requireNonNull(notAfter, "notAfter");
return this; return this;
@ -162,17 +162,17 @@ public class OrderBuilder {
* Enables short-term automatic renewal of the certificate. Must be supported by the * Enables short-term automatic renewal of the certificate. Must be supported by the
* CA. * CA.
* <p> * <p>
* Recurrent renewals cannot be combined with {@link #notBefore(Instant)} or * Automatic renewals cannot be combined with {@link #notBefore(Instant)} or {@link
* {@link #notAfter(Instant)}. * #notAfter(Instant)}.
* *
* @return itself * @return itself
* @since 2.3 * @since 2.3
*/ */
public OrderBuilder recurrent() { public OrderBuilder autoRenewal() {
if (notBefore != null || notAfter != null) { if (notBefore != null || notAfter != null) {
throw new IllegalArgumentException("cannot combine notBefore/notAfter with recurrent"); throw new IllegalArgumentException("cannot combine notBefore/notAfter with autoRenewalOr");
} }
this.recurrent = true; this.autoRenewal = true;
return this; return this;
} }
@ -180,16 +180,16 @@ public class OrderBuilder {
* Sets the earliest date of validity of the first issued certificate. If not set, * Sets the earliest date of validity of the first issued certificate. If not set,
* the start date is the earliest possible date. * the start date is the earliest possible date.
* <p> * <p>
* Implies {@link #recurrent()}. * Implies {@link #autoRenewal()}.
* *
* @param start * @param start
* Start date of validity * Start date of validity
* @return itself * @return itself
* @since 2.3 * @since 2.3
*/ */
public OrderBuilder recurrentStart(Instant start) { public OrderBuilder autoRenewalStart(Instant start) {
recurrent(); autoRenewal();
this.recurrentStart = requireNonNull(start, "start"); this.autoRenewalStart = requireNonNull(start, "start");
return this; return this;
} }
@ -197,17 +197,17 @@ public class OrderBuilder {
* Sets the latest date of validity of the last issued certificate. If not set, the * Sets the latest date of validity of the last issued certificate. If not set, the
* CA's default is used. * CA's default is used.
* <p> * <p>
* Implies {@link #recurrent()}. * Implies {@link #autoRenewal()}.
* *
* @param end * @param end
* End date of validity * End date of validity
* @return itself * @return itself
* @see Metadata#getStarMaxRenewal() * @see Metadata#getAutoRenewalMaxDuration()
* @since 2.3 * @since 2.3
*/ */
public OrderBuilder recurrentEnd(Instant end) { public OrderBuilder autoRenewalEnd(Instant end) {
recurrent(); autoRenewal();
this.recurrentEnd = requireNonNull(end, "end"); this.autoRenewalEnd = requireNonNull(end, "end");
return this; return this;
} }
@ -215,17 +215,17 @@ public class OrderBuilder {
* Sets the maximum validity period of each certificate. If not set, the CA's * Sets the maximum validity period of each certificate. If not set, the CA's
* default is used. * default is used.
* <p> * <p>
* Implies {@link #recurrent()}. * Implies {@link #autoRenewal()}.
* *
* @param duration * @param duration
* Duration of validity of each certificate * Duration of validity of each certificate
* @return itself * @return itself
* @see Metadata#getStarMinCertValidity() * @see Metadata#getAutoRenewalMinLifetime()
* @since 2.3 * @since 2.3
*/ */
public OrderBuilder recurrentCertificateValidity(Duration duration) { public OrderBuilder autoRenewalLifetime(Duration duration) {
recurrent(); autoRenewal();
this.recurrentValidity = requireNonNull(duration, "duration"); this.autoRenewalLifetime = requireNonNull(duration, "duration");
return this; return this;
} }
@ -233,36 +233,36 @@ public class OrderBuilder {
* Sets the amount of pre-dating each certificate. If not set, the CA's * Sets the amount of pre-dating each certificate. If not set, the CA's
* default (0) is used. * default (0) is used.
* <p> * <p>
* Implies {@link #recurrent()}. * Implies {@link #autoRenewal()}.
* *
* @param duration * @param duration
* Duration of certificate pre-dating * Duration of certificate pre-dating
* @return itself * @return itself
* @since 2.7 * @since 2.7
*/ */
public OrderBuilder recurrentCertificatePredate(Duration duration) { public OrderBuilder autoRenewalLifetimeAdjust(Duration duration) {
recurrent(); autoRenewal();
this.recurrentPredate = requireNonNull(duration, "duration"); this.autoRenewalLifetimeAdjust = requireNonNull(duration, "duration");
return this; return this;
} }
/** /**
* Announces that the client wishes to fetch the recurring certificate via GET * Announces that the client wishes to fetch the auto-renewed certificate via GET
* request. If not used, the STAR certificate can only be fetched via POST-as-GET * request. If not used, the STAR certificate can only be fetched via POST-as-GET
* request. {@link Metadata#isStarCertificateGetAllowed()} must return {@code true} in * request. {@link Metadata#isAutoRenewalGetAllowed()} must return {@code true} in
* order for this option to work. * order for this option to work.
* <p> * <p>
* This option is only needed if you plan to fetch the STAR certificate via other * This option is only needed if you plan to fetch the STAR certificate via other
* means than by using acme4j. * means than by using acme4j.
* <p> * <p>
* Implies {@link #recurrent()}. * Implies {@link #autoRenewal()}.
* *
* @return itself * @return itself
* @since 2.6 * @since 2.6
*/ */
public OrderBuilder recurrentEnableGet() { public OrderBuilder autoRenewalEnableGet() {
recurrent(); autoRenewal();
this.recurrentGet = true; this.autoRenewalGet = true;
return this; return this;
} }
@ -278,7 +278,7 @@ public class OrderBuilder {
Session session = login.getSession(); Session session = login.getSession();
if (recurrent && !session.getMetadata().isStarEnabled()) { if (autoRenewal && !session.getMetadata().isAutoRenewalEnabled()) {
throw new AcmeException("CA does not support short-term automatic renewals"); throw new AcmeException("CA does not support short-term automatic renewals");
} }
@ -294,22 +294,22 @@ public class OrderBuilder {
claims.put("notAfter", notAfter); claims.put("notAfter", notAfter);
} }
if (recurrent) { if (autoRenewal) {
claims.put("recurrent", true); JSONBuilder arClaims = claims.object("auto-renewal");
if (recurrentStart != null) { if (autoRenewalStart != null) {
claims.put("recurrent-start-date", recurrentStart); arClaims.put("start-date", autoRenewalStart);
} }
if (recurrentStart != null) { if (autoRenewalStart != null) {
claims.put("recurrent-end-date", recurrentEnd); arClaims.put("end-date", autoRenewalEnd);
} }
if (recurrentValidity != null) { if (autoRenewalLifetime != null) {
claims.put("recurrent-certificate-validity", recurrentValidity); arClaims.put("lifetime", autoRenewalLifetime);
} }
if (recurrentPredate != null) { if (autoRenewalLifetimeAdjust != null) {
claims.put("recurrent-certificate-predate", recurrentPredate); arClaims.put("lifetime-adjust", autoRenewalLifetimeAdjust);
} }
if (recurrentGet) { if (autoRenewalGet) {
claims.put("recurrent-certificate-get", recurrentGet); arClaims.put("allow-certificate-get", autoRenewalGet);
} }
} }

View File

@ -68,7 +68,7 @@ public enum Status {
EXPIRED, EXPIRED,
/** /**
* A recurrent {@link Order} is canceled. * An auto-renewing {@link Order} is canceled.
* *
* @since 2.3 * @since 2.3
*/ */

View File

@ -102,6 +102,12 @@ public class OrderBuilderTest {
assertThat(order.getNotAfter(), is(parseTimestamp("2016-01-08T00:10:00Z"))); assertThat(order.getNotAfter(), is(parseTimestamp("2016-01-08T00:10:00Z")));
assertThat(order.getExpires(), is(parseTimestamp("2016-01-10T00:00:00Z"))); assertThat(order.getExpires(), is(parseTimestamp("2016-01-10T00:00:00Z")));
assertThat(order.getStatus(), is(Status.PENDING)); assertThat(order.getStatus(), is(Status.PENDING));
assertThat(order.isAutoRenewing(), is(false));
assertThat(order.getAutoRenewalStartDate(), is(nullValue()));
assertThat(order.getAutoRenewalEndDate(), is(nullValue()));
assertThat(order.getAutoRenewalLifetime(), is(nullValue()));
assertThat(order.getAutoRenewalLifetimeAdjust(), is(nullValue()));
assertThat(order.isAutoRenewalGetEnabled(), is(false));
assertThat(order.getLocation(), is(locationUrl)); assertThat(order.getLocation(), is(locationUrl));
assertThat(order.getAuthorizations(), is(notNullValue())); assertThat(order.getAuthorizations(), is(notNullValue()));
assertThat(order.getAuthorizations().size(), is(2)); assertThat(order.getAuthorizations().size(), is(2));
@ -110,12 +116,12 @@ public class OrderBuilderTest {
} }
/** /**
* Test that a new recurrent {@link Order} can be created. * Test that a new auto-renewal {@link Order} can be created.
*/ */
@Test @Test
public void testRecurrentOrderCertificate() throws Exception { public void testAutoRenewOrderCertificate() throws Exception {
Instant recurrentStart = parseTimestamp("2018-01-01T00:00:00Z"); Instant autoRenewStart = parseTimestamp("2018-01-01T00:00:00Z");
Instant recurrentEnd = parseTimestamp("2019-01-01T00:00:00Z"); Instant autoRenewEnd = parseTimestamp("2019-01-01T00:00:00Z");
Duration validity = Duration.ofDays(7); Duration validity = Duration.ofDays(7);
Duration predate = Duration.ofDays(6); Duration predate = Duration.ofDays(6);
@ -123,14 +129,14 @@ public class OrderBuilderTest {
@Override @Override
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) { public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(resourceUrl)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("requestRecurrentOrderRequest").toString())); assertThat(claims.toString(), sameJSONAs(getJSON("requestAutoRenewOrderRequest").toString()));
assertThat(login, is(notNullValue())); assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_CREATED; return HttpURLConnection.HTTP_CREATED;
} }
@Override @Override
public JSON readJsonResponse() { public JSON readJsonResponse() {
return getJSON("requestRecurrentOrderResponse"); return getJSON("requestAutoRenewOrderResponse");
} }
@Override @Override
@ -141,39 +147,39 @@ public class OrderBuilderTest {
Login login = provider.createLogin(); Login login = provider.createLogin();
provider.putMetadata("star-enabled", true); provider.putMetadata("auto-renewal", JSON.empty());
provider.putTestResource(Resource.NEW_ORDER, resourceUrl); provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
Account account = new Account(login); Account account = new Account(login);
Order order = account.newOrder() Order order = account.newOrder()
.domain("example.org") .domain("example.org")
.recurrent() .autoRenewal()
.recurrentStart(recurrentStart) .autoRenewalStart(autoRenewStart)
.recurrentEnd(recurrentEnd) .autoRenewalEnd(autoRenewEnd)
.recurrentCertificateValidity(validity) .autoRenewalLifetime(validity)
.recurrentCertificatePredate(predate) .autoRenewalLifetimeAdjust(predate)
.recurrentEnableGet() .autoRenewalEnableGet()
.create(); .create();
assertThat(order.getIdentifiers(), containsInAnyOrder(Identifier.dns("example.org"))); assertThat(order.getIdentifiers(), containsInAnyOrder(Identifier.dns("example.org")));
assertThat(order.getNotBefore(), is(nullValue())); assertThat(order.getNotBefore(), is(nullValue()));
assertThat(order.getNotAfter(), is(nullValue())); assertThat(order.getNotAfter(), is(nullValue()));
assertThat(order.isRecurrent(), is(true)); assertThat(order.isAutoRenewing(), is(true));
assertThat(order.getRecurrentStart(), is(recurrentStart)); assertThat(order.getAutoRenewalStartDate(), is(autoRenewStart));
assertThat(order.getRecurrentEnd(), is(recurrentEnd)); assertThat(order.getAutoRenewalEndDate(), is(autoRenewEnd));
assertThat(order.getRecurrentCertificateValidity(), is(validity)); assertThat(order.getAutoRenewalLifetime(), is(validity));
assertThat(order.getRecurrentCertificatePredate(), is(predate)); assertThat(order.getAutoRenewalLifetimeAdjust(), is(predate));
assertThat(order.isRecurrentGetEnabled(), is(true)); assertThat(order.isAutoRenewalGetEnabled(), is(true));
assertThat(order.getLocation(), is(locationUrl)); assertThat(order.getLocation(), is(locationUrl));
provider.close(); provider.close();
} }
/** /**
* Test that a recurrent {@link Order} cannot be created if unsupported by the CA. * Test that an auto-renewal {@link Order} cannot be created if unsupported by the CA.
*/ */
@Test(expected = AcmeException.class) @Test(expected = AcmeException.class)
public void testRecurrentOrderCertificateFails() throws Exception { public void testAutoRenewOrderCertificateFails() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider(); TestableConnectionProvider provider = new TestableConnectionProvider();
provider.putTestResource(Resource.NEW_ORDER, resourceUrl); provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
@ -182,17 +188,17 @@ public class OrderBuilderTest {
Account account = new Account(login); Account account = new Account(login);
account.newOrder() account.newOrder()
.domain("example.org") .domain("example.org")
.recurrent() .autoRenewal()
.create(); .create();
provider.close(); provider.close();
} }
/** /**
* Test that recurrent and notBefore/notAfter cannot be mixed. * Test that auto-renew and notBefore/notAfter cannot be mixed.
*/ */
@Test @Test
public void testRecurrentNotMixed() throws Exception { public void testAutoRenewNotMixed() throws Exception {
Instant someInstant = parseTimestamp("2018-01-01T00:00:00Z"); Instant someInstant = parseTimestamp("2018-01-01T00:00:00Z");
TestableConnectionProvider provider = new TestableConnectionProvider(); TestableConnectionProvider provider = new TestableConnectionProvider();
@ -201,7 +207,7 @@ public class OrderBuilderTest {
Account account = new Account(login); Account account = new Account(login);
try { try {
OrderBuilder ob = account.newOrder().recurrent(); OrderBuilder ob = account.newOrder().autoRenewal();
ob.notBefore(someInstant); ob.notBefore(someInstant);
fail("accepted notBefore"); fail("accepted notBefore");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
@ -209,7 +215,7 @@ public class OrderBuilderTest {
} }
try { try {
OrderBuilder ob = account.newOrder().recurrent(); OrderBuilder ob = account.newOrder().autoRenewal();
ob.notAfter(someInstant); ob.notAfter(someInstant);
fail("accepted notAfter"); fail("accepted notAfter");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
@ -218,32 +224,32 @@ public class OrderBuilderTest {
try { try {
OrderBuilder ob = account.newOrder().notBefore(someInstant); OrderBuilder ob = account.newOrder().notBefore(someInstant);
ob.recurrent(); ob.autoRenewal();
fail("accepted recurrent"); fail("accepted autoRenewal");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// expected // expected
} }
try { try {
OrderBuilder ob = account.newOrder().notBefore(someInstant); OrderBuilder ob = account.newOrder().notBefore(someInstant);
ob.recurrentStart(someInstant); ob.autoRenewalStart(someInstant);
fail("accepted recurrentStart"); fail("accepted autoRenewalStart");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// expected // expected
} }
try { try {
OrderBuilder ob = account.newOrder().notBefore(someInstant); OrderBuilder ob = account.newOrder().notBefore(someInstant);
ob.recurrentEnd(someInstant); ob.autoRenewalEnd(someInstant);
fail("accepted recurrentEnd"); fail("accepted autoRenewalEnd");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// expected // expected
} }
try { try {
OrderBuilder ob = account.newOrder().notBefore(someInstant); OrderBuilder ob = account.newOrder().notBefore(someInstant);
ob.recurrentCertificateValidity(Duration.ofDays(7)); ob.autoRenewalLifetime(Duration.ofDays(7));
fail("accepted recurrentCertificateValidity"); fail("accepted autoRenewalLifetime");
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// expected // expected
} }

View File

@ -81,12 +81,12 @@ public class OrderTest {
assertThat(order.getCertificate().getLocation(), is(url("https://example.com/acme/cert/1234"))); assertThat(order.getCertificate().getLocation(), is(url("https://example.com/acme/cert/1234")));
assertThat(order.getFinalizeLocation(), is(finalizeUrl)); assertThat(order.getFinalizeLocation(), is(finalizeUrl));
assertThat(order.isRecurrent(), is(false)); assertThat(order.isAutoRenewing(), is(false));
assertThat(order.getRecurrentStart(), is(nullValue())); assertThat(order.getAutoRenewalStartDate(), is(nullValue()));
assertThat(order.getRecurrentEnd(), is(nullValue())); assertThat(order.getAutoRenewalEndDate(), is(nullValue()));
assertThat(order.getRecurrentCertificateValidity(), is(nullValue())); assertThat(order.getAutoRenewalLifetime(), is(nullValue()));
assertThat(order.getRecurrentCertificatePredate(), is(nullValue())); assertThat(order.getAutoRenewalLifetimeAdjust(), is(nullValue()));
assertThat(order.isRecurrentGetEnabled(), is(false)); assertThat(order.isAutoRenewalGetEnabled(), is(false));
assertThat(order.getError(), is(notNullValue())); assertThat(order.getError(), is(notNullValue()));
assertThat(order.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); assertThat(order.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection")));
@ -198,7 +198,7 @@ public class OrderTest {
assertThat(order.getNotBefore(), is(parseTimestamp("2016-01-01T00:00:00Z"))); assertThat(order.getNotBefore(), is(parseTimestamp("2016-01-01T00:00:00Z")));
assertThat(order.getNotAfter(), is(parseTimestamp("2016-01-08T00:00:00Z"))); assertThat(order.getNotAfter(), is(parseTimestamp("2016-01-08T00:00:00Z")));
assertThat(order.getCertificate().getLocation(), is(url("https://example.com/acme/cert/1234"))); assertThat(order.getCertificate().getLocation(), is(url("https://example.com/acme/cert/1234")));
assertThat(order.getStarCertificate(), is(nullValue())); assertThat(order.getAutoRenewalCertificate(), is(nullValue()));
assertThat(order.getFinalizeLocation(), is(finalizeUrl)); assertThat(order.getFinalizeLocation(), is(finalizeUrl));
List<Authorization> auths = order.getAuthorizations(); List<Authorization> auths = order.getAuthorizations();
@ -215,7 +215,7 @@ public class OrderTest {
* Test that order is properly updated. * Test that order is properly updated.
*/ */
@Test @Test
public void testRecurrentUpdate() throws Exception { public void testAutoRenewUpdate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public int sendSignedPostAsGetRequest(URL url, Login login) { public int sendSignedPostAsGetRequest(URL url, Login login) {
@ -225,7 +225,7 @@ public class OrderTest {
@Override @Override
public JSON readJsonResponse() { public JSON readJsonResponse() {
return getJSON("updateRecurrentOrderResponse"); return getJSON("updateAutoRenewOrderResponse");
} }
@Override @Override
@ -234,30 +234,30 @@ public class OrderTest {
} }
}; };
provider.putMetadata("star-enabled", true); provider.putMetadata("auto-renewal", JSON.empty());
Login login = provider.createLogin(); Login login = provider.createLogin();
Order order = new Order(login, locationUrl); Order order = new Order(login, locationUrl);
order.update(); order.update();
assertThat(order.isRecurrent(), is(true)); assertThat(order.isAutoRenewing(), is(true));
assertThat(order.getRecurrentStart(), is(parseTimestamp("2016-01-01T00:00:00Z"))); assertThat(order.getAutoRenewalStartDate(), is(parseTimestamp("2016-01-01T00:00:00Z")));
assertThat(order.getRecurrentEnd(), is(parseTimestamp("2017-01-01T00:00:00Z"))); assertThat(order.getAutoRenewalEndDate(), is(parseTimestamp("2017-01-01T00:00:00Z")));
assertThat(order.getRecurrentCertificateValidity(), is(Duration.ofHours(168))); assertThat(order.getAutoRenewalLifetime(), is(Duration.ofHours(168)));
assertThat(order.getRecurrentCertificatePredate(), is(Duration.ofDays(6))); assertThat(order.getAutoRenewalLifetimeAdjust(), is(Duration.ofDays(6)));
assertThat(order.getNotBefore(), is(nullValue())); assertThat(order.getNotBefore(), is(nullValue()));
assertThat(order.getNotAfter(), is(nullValue())); assertThat(order.getNotAfter(), is(nullValue()));
assertThat(order.isRecurrentGetEnabled(), is(true)); assertThat(order.isAutoRenewalGetEnabled(), is(true));
provider.close(); provider.close();
} }
/** /**
* Test that recurrent order is properly finalized. * Test that auto-renew order is properly finalized.
*/ */
@Test @Test
public void testRecurrentFinalize() throws Exception { public void testAutoRenewFinalize() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public int sendSignedPostAsGetRequest(URL url, Login login) { public int sendSignedPostAsGetRequest(URL url, Login login) {
@ -267,7 +267,7 @@ public class OrderTest {
@Override @Override
public JSON readJsonResponse() { public JSON readJsonResponse() {
return getJSON("finalizeRecurrentResponse"); return getJSON("finalizeAutoRenewResponse");
} }
@Override @Override
@ -280,21 +280,21 @@ public class OrderTest {
Order order = login.bindOrder(locationUrl); Order order = login.bindOrder(locationUrl);
assertThat(order.getCertificate(), is(nullValue())); assertThat(order.getCertificate(), is(nullValue()));
assertThat(order.getStarCertificate().getLocation(), is(url("https://example.com/acme/cert/1234"))); assertThat(order.getAutoRenewalCertificate().getLocation(), is(url("https://example.com/acme/cert/1234")));
assertThat(order.isRecurrent(), is(true)); assertThat(order.isAutoRenewing(), is(true));
assertThat(order.getRecurrentStart(), is(parseTimestamp("2018-01-01T00:00:00Z"))); assertThat(order.getAutoRenewalStartDate(), is(parseTimestamp("2018-01-01T00:00:00Z")));
assertThat(order.getRecurrentEnd(), is(parseTimestamp("2019-01-01T00:00:00Z"))); assertThat(order.getAutoRenewalEndDate(), is(parseTimestamp("2019-01-01T00:00:00Z")));
assertThat(order.getRecurrentCertificateValidity(), is(Duration.ofHours(168))); assertThat(order.getAutoRenewalLifetime(), is(Duration.ofHours(168)));
assertThat(order.getRecurrentCertificatePredate(), is(Duration.ofDays(6))); assertThat(order.getAutoRenewalLifetimeAdjust(), is(Duration.ofDays(6)));
assertThat(order.getNotBefore(), is(nullValue())); assertThat(order.getNotBefore(), is(nullValue()));
assertThat(order.getNotAfter(), is(nullValue())); assertThat(order.getNotAfter(), is(nullValue()));
assertThat(order.isRecurrentGetEnabled(), is(true)); assertThat(order.isAutoRenewalGetEnabled(), is(true));
provider.close(); provider.close();
} }
/** /**
* Test that recurrent order is properly canceled. * Test that auto-renew order is properly canceled.
*/ */
@Test @Test
public void testCancel() throws Exception { public void testCancel() throws Exception {
@ -314,12 +314,12 @@ public class OrderTest {
} }
}; };
provider.putMetadata("star-enabled", true); provider.putMetadata("auto-renewal", JSON.empty());
Login login = provider.createLogin(); Login login = provider.createLogin();
Order order = new Order(login, locationUrl); Order order = new Order(login, locationUrl);
order.cancelRecurrent(); order.cancelAutoRenewal();
assertThat(order.getStatus(), is(Status.CANCELED)); assertThat(order.getStatus(), is(Status.CANCELED));

View File

@ -184,10 +184,10 @@ public class SessionTest {
assertThat(meta.getTermsOfService(), is(nullValue())); assertThat(meta.getTermsOfService(), is(nullValue()));
assertThat(meta.getWebsite(), is(nullValue())); assertThat(meta.getWebsite(), is(nullValue()));
assertThat(meta.getCaaIdentities(), is(empty())); assertThat(meta.getCaaIdentities(), is(empty()));
assertThat(meta.isStarEnabled(), is(false)); assertThat(meta.isAutoRenewalEnabled(), is(false));
assertThat(meta.getStarMaxRenewal(), is(nullValue())); assertThat(meta.getAutoRenewalMaxDuration(), is(nullValue()));
assertThat(meta.getStarMinCertValidity(), is(nullValue())); assertThat(meta.getAutoRenewalMinLifetime(), is(nullValue()));
assertThat(meta.isStarCertificateGetAllowed(), is(false)); assertThat(meta.isAutoRenewalGetAllowed(), is(false));
} }
/** /**
@ -217,10 +217,10 @@ public class SessionTest {
assertThat(meta.getTermsOfService(), is(URI.create("https://example.com/acme/terms"))); assertThat(meta.getTermsOfService(), is(URI.create("https://example.com/acme/terms")));
assertThat(meta.getWebsite(), is(url("https://www.example.com/"))); assertThat(meta.getWebsite(), is(url("https://www.example.com/")));
assertThat(meta.getCaaIdentities(), containsInAnyOrder("example.com")); assertThat(meta.getCaaIdentities(), containsInAnyOrder("example.com"));
assertThat(meta.isStarEnabled(), is(true)); assertThat(meta.isAutoRenewalEnabled(), is(true));
assertThat(meta.getStarMaxRenewal(), is(Duration.ofDays(365))); assertThat(meta.getAutoRenewalMaxDuration(), is(Duration.ofDays(365)));
assertThat(meta.getStarMinCertValidity(), is(Duration.ofHours(24))); assertThat(meta.getAutoRenewalMinLifetime(), is(Duration.ofHours(24)));
assertThat(meta.isStarCertificateGetAllowed(), is(true)); assertThat(meta.isAutoRenewalGetAllowed(), is(true));
assertThat(meta.isExternalAccountRequired(), is(true)); assertThat(meta.isExternalAccountRequired(), is(true));
assertThat(meta.getJSON(), is(notNullValue())); assertThat(meta.getJSON(), is(notNullValue()));
} }

View File

@ -9,11 +9,12 @@
"caaIdentities": [ "caaIdentities": [
"example.com" "example.com"
], ],
"auto-renewal": {
"min-lifetime": 86400,
"max-duration": 31536000,
"allow-certificate-get": true
},
"externalAccountRequired": true, "externalAccountRequired": true,
"star-enabled": true,
"star-min-cert-validity": 86400,
"star-max-renewal": 31536000,
"star-allow-certificate-get": true,
"xTestString": "foobar", "xTestString": "foobar",
"xTestUri": "https://www.example.org", "xTestUri": "https://www.example.org",
"xTestArray": [ "xTestArray": [

View File

@ -11,12 +11,13 @@
"value": "www.example.com" "value": "www.example.com"
} }
], ],
"recurrent": true, "auto-renewal": {
"recurrent-start-date": "2018-01-01T00:00:00Z", "start-date": "2018-01-01T00:00:00Z",
"recurrent-end-date": "2019-01-01T00:00:00Z", "end-date": "2019-01-01T00:00:00Z",
"recurrent-certificate-validity": 604800, "lifetime": 604800,
"recurrent-certificate-predate": 518400, "lifetime-adjust": 518400,
"recurrent-certificate-get": true, "allow-certificate-get": true
},
"authorizations": [ "authorizations": [
"https://example.com/acme/authz/1234", "https://example.com/acme/authz/1234",
"https://example.com/acme/authz/2345" "https://example.com/acme/authz/2345"

View File

@ -0,0 +1,15 @@
{
"identifiers": [
{
"type": "dns",
"value": "example.org"
}
],
"auto-renewal": {
"start-date": "2018-01-01T00:00:00Z",
"end-date": "2019-01-01T00:00:00Z",
"lifetime": 604800,
"lifetime-adjust": 518400,
"allow-certificate-get": true
}
}

View File

@ -7,12 +7,13 @@
"value": "example.org" "value": "example.org"
} }
], ],
"recurrent": true, "auto-renewal": {
"recurrent-start-date": "2018-01-01T00:00:00Z", "start-date": "2018-01-01T00:00:00Z",
"recurrent-end-date": "2019-01-01T00:00:00Z", "end-date": "2019-01-01T00:00:00Z",
"recurrent-certificate-validity": 604800, "lifetime": 604800,
"recurrent-certificate-predate": 518400, "lifetime-adjust": 518400,
"recurrent-certificate-get": true, "allow-certificate-get": true
},
"authorizations": [ "authorizations": [
"https://example.com/acme/authz/1234", "https://example.com/acme/authz/1234",
"https://example.com/acme/authz/2345" "https://example.com/acme/authz/2345"

View File

@ -1,14 +0,0 @@
{
"identifiers": [
{
"type": "dns",
"value": "example.org"
}
],
"recurrent": true,
"recurrent-start-date": "2018-01-01T00:00:00Z",
"recurrent-end-date": "2019-01-01T00:00:00Z",
"recurrent-certificate-validity": 604800,
"recurrent-certificate-predate": 518400,
"recurrent-certificate-get": true
}

View File

@ -0,0 +1,10 @@
{
"status": "valid",
"auto-renewal": {
"start-date": "2016-01-01T00:00:00Z",
"end-date": "2017-01-01T00:00:00Z",
"lifetime": 604800,
"lifetime-adjust": 518400,
"allow-certificate-get": true
}
}

View File

@ -1,9 +0,0 @@
{
"status": "valid",
"recurrent": true,
"recurrent-start-date": "2016-01-01T00:00:00Z",
"recurrent-end-date": "2017-01-01T00:00:00Z",
"recurrent-certificate-validity": 604800,
"recurrent-certificate-predate": 518400,
"recurrent-certificate-get": true
}

View File

@ -2,6 +2,10 @@
This document will help you migrate your code to the latest _acme4j_ version. This document will help you migrate your code to the latest _acme4j_ version.
## Migration to Version 2.9
- In the ACME STAR draft 09, the term "recurring" has been changed to "auto-renewal". To reflect this change, all STAR related methods in the acme4j API have been renamed as well. If you are using the STAR extension, you are going to get a number of compile errors, but you will always find a corresponding new method. No functionality has been removed. I decided to do a hard API change because acme4j's STAR support is still experimental.
## Migration to Version 2.8 ## Migration to Version 2.8
- Challenges can now be found by their class type instead of a type string, which makes finding a challenge type safe. I recommend to migrate your code to this new way. The classic way is not deprecated and will not be removed though. Example: - Challenges can now be found by their class type instead of a type string, which makes finding a challenge type safe. I recommend to migrate your code to this new way. The classic way is not deprecated and will not be removed though. Example:

View File

@ -198,25 +198,25 @@ _acme4j_ supports the [ACME STAR](https://tools.ietf.org/html/draft-ietf-acme-st
To find out if the CA supports the STAR extension, check the metadata: To find out if the CA supports the STAR extension, check the metadata:
```java ```java
if (session.getMetadata().isStarEnabled()) { if (session.getMetadata().isAutoRenewalEnabled()) {
// CA supports STAR! // CA supports STAR!
} }
``` ```
If STAR is supported, you can enable recurrent renewals by adding `recurrent()` to the order parameters: If STAR is supported, you can enable automatic renewals by adding `autoRenewal()` to the order parameters:
```java ```java
Order order = account.newOrder() Order order = account.newOrder()
.domain("example.org") .domain("example.org")
.recurrent() .autoRenewal()
.create(); .create();
``` ```
You can use `recurrentStart()`, `recurrentEnd()`, `recurrentCertificateValidity()` and `recurrentCertificatePredate()` to change the time span and frequency of automatic renewals. You cannot use `notBefore()` and `notAfter()` in combination with `recurrent()` though. You can use `autoRenewalStart()`, `autoRenewalEnd()`, `autoRenewalLifetime()` and `autoRenewalLifetimeAdjust()` to change the time span and frequency of automatic renewals. You cannot use `notBefore()` and `notAfter()` in combination with `autoRenewal()` though.
The `Metadata` object also holds the accepted renewal limits (see `Metadata.getStarMinCertValidity()` and `Metadata.getStarMaxRenewal()`). The `Metadata` object also holds the accepted renewal limits (see `Metadata.getAutoRenewalMinLifetime()` and `Metadata.getAutoRenewalMaxDuration()`).
After the validation process is completed and the order is finalized, the STAR certificate is available via `Order.getStarCertificate()` (_not_ `Order.getCertificate()`)! After the validation process is completed and the order is finalized, the STAR certificate is available via `Order.getAutoRenewalCertificate()` (_not_ `Order.getCertificate()`)!
Use `Certificate.getLocation()` to retrieve the URL of your certificate. It is renewed automatically, so you will always be able to download the latest issue of the certificate from this URL. Use `Certificate.getLocation()` to retrieve the URL of your certificate. It is renewed automatically, so you will always be able to download the latest issue of the certificate from this URL.
@ -233,6 +233,6 @@ X509Certificate latestCertificate = cert.getCertificate();
``` ```
If supported by the CA, it is possible to negotiate that the certificate can also be downloaded via `GET` request. First use `Metadata.isStarCertificateGetAllowed()` to check if this option is supported by the CA. If it is, add `recurrentEnableGet()` to the order parameters to enable it. After the order was finalized, you can use any HTTP client to download the latest certificate from the certificate URL by a `GET` request. If supported by the CA, it is possible to negotiate that the certificate can also be downloaded via `GET` request. First use `Metadata.isAutoRenewalGetAllowed()` to check if this option is supported by the CA. If it is, add `autoRenewalEnableGet()` to the order parameters to enable it. After the order was finalized, you can use any HTTP client to download the latest certificate from the certificate URL by a `GET` request.
Use `Order.cancelRecurrent()` to terminate automatical certificate renewals. Use `Order.cancelAutoRenewal()` to terminate automatical certificate renewals.