mirror of https://github.com/shred/acme4j
Add pre-authorization support
parent
4e1ad652b0
commit
7d83ef0e80
|
@ -37,6 +37,7 @@ import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeServerException;
|
||||||
import org.shredzone.acme4j.provider.pebble.Pebble;
|
import org.shredzone.acme4j.provider.pebble.Pebble;
|
||||||
import org.shredzone.acme4j.util.JSON;
|
import org.shredzone.acme4j.util.JSON;
|
||||||
import org.shredzone.acme4j.util.JSONBuilder;
|
import org.shredzone.acme4j.util.JSONBuilder;
|
||||||
|
@ -176,28 +177,40 @@ public class Registration extends AcmeResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorizes a domain. The domain is associated with this registration.
|
* Pre-authorizes a domain. The CA will check if it accepts the domain for
|
||||||
|
* certification, and returns the necessary challenges.
|
||||||
* <p>
|
* <p>
|
||||||
* IDN domain names will be ACE encoded automatically.
|
* Some servers may not allow pre-authorization.
|
||||||
*
|
*
|
||||||
* @param domain
|
* @param domain
|
||||||
* Domain name to be authorized
|
* Domain name to be pre-authorized. IDN names are accepted and will be ACE
|
||||||
|
* encoded automatically.
|
||||||
* @return {@link Authorization} object for this domain
|
* @return {@link Authorization} object for this domain
|
||||||
|
* @throws AcmeException
|
||||||
|
* if the server does not allow pre-authorization
|
||||||
|
* @throws AcmeServerException
|
||||||
|
* if the server allows pre-authorization, but will refuse to issue a
|
||||||
|
* certificate for this domain
|
||||||
*/
|
*/
|
||||||
public Authorization authorizeDomain(String domain) throws AcmeException {
|
public Authorization preAuthorizeDomain(String domain) throws AcmeException {
|
||||||
Objects.requireNonNull(domain, "domain");
|
Objects.requireNonNull(domain, "domain");
|
||||||
if (domain.isEmpty()) {
|
if (domain.isEmpty()) {
|
||||||
throw new IllegalArgumentException("domain must not be empty");
|
throw new IllegalArgumentException("domain must not be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug("authorizeDomain {}", domain);
|
URL newAuthzUrl = getSession().resourceUrl(Resource.NEW_AUTHZ);
|
||||||
|
if (newAuthzUrl == null) {
|
||||||
|
throw new AcmeException("Server does not allow pre-authorization");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.debug("preAuthorizeDomain {}", domain);
|
||||||
try (Connection conn = getSession().provider().connect()) {
|
try (Connection conn = getSession().provider().connect()) {
|
||||||
JSONBuilder claims = new JSONBuilder();
|
JSONBuilder claims = new JSONBuilder();
|
||||||
claims.object("identifier")
|
claims.object("identifier")
|
||||||
.put("type", "dns")
|
.put("type", "dns")
|
||||||
.put("value", toAce(domain));
|
.put("value", toAce(domain));
|
||||||
|
|
||||||
conn.sendSignedRequest(getSession().resourceUrl(Resource.NEW_AUTHZ), claims, getSession());
|
conn.sendSignedRequest(newAuthzUrl, claims, getSession());
|
||||||
conn.accept(HttpURLConnection.HTTP_CREATED);
|
conn.accept(HttpURLConnection.HTTP_CREATED);
|
||||||
|
|
||||||
JSON json = conn.readJsonResponse();
|
JSON json = conn.readJsonResponse();
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.shredzone.acme4j.challenge.Dns01Challenge;
|
||||||
import org.shredzone.acme4j.challenge.Http01Challenge;
|
import org.shredzone.acme4j.challenge.Http01Challenge;
|
||||||
import org.shredzone.acme4j.connector.Resource;
|
import org.shredzone.acme4j.connector.Resource;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeServerException;
|
||||||
import org.shredzone.acme4j.provider.AcmeProvider;
|
import org.shredzone.acme4j.provider.AcmeProvider;
|
||||||
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
||||||
import org.shredzone.acme4j.util.JSON;
|
import org.shredzone.acme4j.util.JSON;
|
||||||
|
@ -251,10 +252,10 @@ public class RegistrationTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that a new {@link Authorization} can be created.
|
* Test that a domain can be pre-authorized.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAuthorizeDomain() throws Exception {
|
public void testPreAuthorizeDomain() throws Exception {
|
||||||
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||||
@Override
|
@Override
|
||||||
public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
|
public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
|
||||||
|
@ -292,7 +293,7 @@ public class RegistrationTest {
|
||||||
String domainName = "example.org";
|
String domainName = "example.org";
|
||||||
|
|
||||||
Registration registration = new Registration(session, locationUrl);
|
Registration registration = new Registration(session, locationUrl);
|
||||||
Authorization auth = registration.authorizeDomain(domainName);
|
Authorization auth = registration.preAuthorizeDomain(domainName);
|
||||||
|
|
||||||
assertThat(auth.getDomain(), is(domainName));
|
assertThat(auth.getDomain(), is(domainName));
|
||||||
assertThat(auth.getStatus(), is(Status.PENDING));
|
assertThat(auth.getStatus(), is(Status.PENDING));
|
||||||
|
@ -305,29 +306,80 @@ public class RegistrationTest {
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a domain pre-authorization can fail.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNoPreAuthorizeDomain() throws Exception {
|
||||||
|
URI problemType = URI.create("urn:ietf:params:acme:error:rejectedIdentifier");
|
||||||
|
String problemDetail = "example.org is blacklisted";
|
||||||
|
|
||||||
|
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||||
|
@Override
|
||||||
|
public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
|
||||||
|
assertThat(url, is(resourceUrl));
|
||||||
|
assertThat(claims.toString(), sameJSONAs(getJSON("newAuthorizationRequest").toString()));
|
||||||
|
assertThat(session, is(notNullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int accept(int... httpStatus) throws AcmeException {
|
||||||
|
Problem problem = TestUtils.createProblem(problemType, problemDetail, resourceUrl);
|
||||||
|
throw new AcmeServerException(problem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Session session = provider.createSession();
|
||||||
|
|
||||||
|
provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl);
|
||||||
|
|
||||||
|
Registration registration = new Registration(session, locationUrl);
|
||||||
|
|
||||||
|
try {
|
||||||
|
registration.preAuthorizeDomain("example.org");
|
||||||
|
fail("preauthorization was accepted");
|
||||||
|
} catch (AcmeServerException ex) {
|
||||||
|
assertThat(ex.getType(), is(problemType));
|
||||||
|
assertThat(ex.getMessage(), is(problemDetail));
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.close();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that a bad domain parameter is not accepted.
|
* Test that a bad domain parameter is not accepted.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAuthorizeBadDomain() throws Exception {
|
public void testAuthorizeBadDomain() throws Exception {
|
||||||
TestableConnectionProvider provider = new TestableConnectionProvider();
|
TestableConnectionProvider provider = new TestableConnectionProvider();
|
||||||
|
// just provide a resource record so the provider returns a directory
|
||||||
|
provider.putTestResource(Resource.NEW_NONCE, resourceUrl);
|
||||||
|
|
||||||
Session session = provider.createSession();
|
Session session = provider.createSession();
|
||||||
Registration registration = Registration.bind(session, locationUrl);
|
Registration registration = Registration.bind(session, locationUrl);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
registration.authorizeDomain(null);
|
registration.preAuthorizeDomain(null);
|
||||||
fail("null domain was accepted");
|
fail("null domain was accepted");
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
registration.authorizeDomain("");
|
registration.preAuthorizeDomain("");
|
||||||
fail("empty domain string was accepted");
|
fail("empty domain string was accepted");
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
registration.preAuthorizeDomain("example.com");
|
||||||
|
fail("preauthorization was accepted");
|
||||||
|
} catch (AcmeException ex) {
|
||||||
|
// expected
|
||||||
|
assertThat(ex.getMessage(), is("Server does not allow pre-authorization"));
|
||||||
|
}
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -209,7 +209,7 @@ public class ClientTest {
|
||||||
*/
|
*/
|
||||||
private void authorize(Registration reg, String domain) throws AcmeException {
|
private void authorize(Registration reg, String domain) throws AcmeException {
|
||||||
// Authorize the domain.
|
// Authorize the domain.
|
||||||
Authorization auth = reg.authorizeDomain(domain);
|
Authorization auth = reg.preAuthorizeDomain(domain);
|
||||||
LOG.info("Authorization for domain " + domain);
|
LOG.info("Authorization for domain " + domain);
|
||||||
|
|
||||||
// Find the desired challenge and prepare it.
|
// Find the desired challenge and prepare it.
|
||||||
|
|
Loading…
Reference in New Issue