Challenge constructor requires JSON data

pull/55/head
Richard Körber 2018-01-14 14:16:20 +01:00
parent 252d886b3f
commit 8923e35d21
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
20 changed files with 153 additions and 121 deletions

View File

@ -176,17 +176,14 @@ public class Session {
public Challenge createChallenge(JSON data) { public Challenge createChallenge(JSON data) {
Objects.requireNonNull(data, "data"); Objects.requireNonNull(data, "data");
String type = data.get("type").required().asString(); Challenge challenge = provider().createChallenge(this, data);
Challenge challenge = provider().createChallenge(this, type);
if (challenge == null) { if (challenge == null) {
if (data.contains("token")) { if (data.contains("token")) {
challenge = new TokenChallenge(this); challenge = new TokenChallenge(this, data);
} else { } else {
challenge = new Challenge(this); challenge = new Challenge(this, data);
} }
} }
challenge.setJSON(data);
return challenge; return challenge;
} }

View File

@ -58,9 +58,11 @@ public class Challenge extends AcmeJsonResource {
* *
* @param session * @param session
* {@link Session} to bind to. * {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/ */
public Challenge(Session session) { public Challenge(Session session, JSON data) {
super(session); super(session, data);
} }
/** /**
@ -159,16 +161,14 @@ public class Challenge extends AcmeJsonResource {
} }
@Override @Override
public void setJSON(JSON json) { protected void setJSON(JSON json) {
String type = json.get(KEY_TYPE).asString(); String type = json.get(KEY_TYPE).required().asString();
if (type == null) {
throw new IllegalArgumentException("map does not contain a type");
}
if (!acceptable(type)) { if (!acceptable(type)) {
throw new AcmeProtocolException("wrong type: " + type); throw new AcmeProtocolException("incompatible type " + type + " for this challenge");
} }
setLocation(json.get(KEY_URL).asURL()); setLocation(json.get(KEY_URL).required().asURL());
super.setJSON(json); super.setJSON(json);
} }

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/** /**
* Implements the {@value TYPE} challenge. * Implements the {@value TYPE} challenge.
@ -33,9 +34,11 @@ public class Dns01Challenge extends TokenChallenge {
* *
* @param session * @param session
* {@link Session} to bind to. * {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/ */
public Dns01Challenge(Session session) { public Dns01Challenge(Session session, JSON data) {
super(session); super(session, data);
} }
/** /**

View File

@ -14,6 +14,7 @@
package org.shredzone.acme4j.challenge; package org.shredzone.acme4j.challenge;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/** /**
* Implements the {@value TYPE} challenge. * Implements the {@value TYPE} challenge.
@ -31,9 +32,11 @@ public class Http01Challenge extends TokenChallenge {
* *
* @param session * @param session
* {@link Session} to bind to. * {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/ */
public Http01Challenge(Session session) { public Http01Challenge(Session session, JSON data) {
super(session); super(session, data);
} }
/** /**

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/** /**
* Implements the {@value TYPE} challenge. * Implements the {@value TYPE} challenge.
@ -33,9 +34,11 @@ public class TlsSni02Challenge extends TokenChallenge {
* *
* @param session * @param session
* {@link Session} to bind to. * {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/ */
public TlsSni02Challenge(Session session) { public TlsSni02Challenge(Session session, JSON data) {
super(session); super(session, data);
} }
/** /**

View File

@ -21,6 +21,7 @@ import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.lang.JoseException; import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder; import org.shredzone.acme4j.toolbox.JSONBuilder;
/** /**
@ -38,9 +39,11 @@ public class TokenChallenge extends Challenge {
* *
* @param session * @param session
* {@link Session} to bind to. * {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/ */
public TokenChallenge(Session session) { public TokenChallenge(Session session, JSON data) {
super(session); super(session, data);
} }
@Override @Override

View File

@ -18,7 +18,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.BiFunction;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
@ -40,7 +40,7 @@ import org.shredzone.acme4j.toolbox.JSON;
*/ */
public abstract class AbstractAcmeProvider implements AcmeProvider { public abstract class AbstractAcmeProvider implements AcmeProvider {
private static final Map<String, Function<Session, Challenge>> CHALLENGES = challengeMap(); private static final Map<String, BiFunction<Session, JSON, Challenge>> CHALLENGES = challengeMap();
@Override @Override
public Connection connect() { public Connection connect() {
@ -59,8 +59,8 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
} }
} }
private static Map<String, Function<Session, Challenge>> challengeMap() { private static Map<String, BiFunction<Session, JSON, Challenge>> challengeMap() {
Map<String, Function<Session, Challenge>> map = new HashMap<>(); Map<String, BiFunction<Session, JSON, Challenge>> map = new HashMap<>();
map.put(Dns01Challenge.TYPE, Dns01Challenge::new); map.put(Dns01Challenge.TYPE, Dns01Challenge::new);
map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new); map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);
@ -69,17 +69,25 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }
/**
* {@inheritDoc}
* <p>
* Custom provider implementations may override this method to provide challenges that
* are unique to the provider.
*/
@Override @Override
public Challenge createChallenge(Session session, String type) { public Challenge createChallenge(Session session, JSON data) {
Objects.requireNonNull(session, "session"); Objects.requireNonNull(session, "session");
Objects.requireNonNull(type, "type"); Objects.requireNonNull(data, "data");
Function<Session, Challenge> constructor = CHALLENGES.get(type); String type = data.get("type").required().asString();
BiFunction<Session, JSON, Challenge> constructor = CHALLENGES.get(type);
if (constructor == null) { if (constructor == null) {
return null; return null;
} }
return constructor.apply(session); return constructor.apply(session, data);
} }
/** /**

View File

@ -75,17 +75,15 @@ public interface AcmeProvider {
JSON directory(Session session, URI serverUri) throws AcmeException; JSON directory(Session session, URI serverUri) throws AcmeException;
/** /**
* Creates a {@link Challenge} instance for the given challenge type. * Creates a {@link Challenge} instance for the given challenge data.
* <p>
* Custom provider implementations may override this method to provide challenges that
* are unique to the provider.
* *
* @param session * @param session
* {@link Session} to bind the challenge to * {@link Session} to bind the challenge to
* @param type * @param data
* Challenge type * Challenge {@link JSON} data
* @return {@link Challenge} instance * @return {@link Challenge} instance, or {@code null} if this provider is unable to
* generate a matching {@link Challenge} instance.
*/ */
Challenge createChallenge(Session session, String type); Challenge createChallenge(Session session, JSON data);
} }

View File

@ -35,7 +35,6 @@ import org.jose4j.jwx.CompactSerializer;
import org.jose4j.lang.JoseException; import org.jose4j.lang.JoseException;
import org.junit.Test; import org.junit.Test;
import org.shredzone.acme4j.Account.EditableAccount; import org.shredzone.acme4j.Account.EditableAccount;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge; 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;
@ -196,12 +195,9 @@ public class AccountTest {
Session session = provider.createSession(); Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(session);
provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl); provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl);
provider.putTestChallenge(Http01Challenge.TYPE, httpChallenge); provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge); provider.putTestChallenge(Dns01Challenge.TYPE, Dns01Challenge::new);
String domainName = "example.org"; String domainName = "example.org";
@ -214,7 +210,8 @@ public class AccountTest {
assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close(); provider.close();
} }

View File

@ -108,10 +108,8 @@ public class AuthorizationTest {
Session session = provider.createSession(); Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session); provider.putTestChallenge("http-01", Http01Challenge::new);
Dns01Challenge dnsChallenge = new Dns01Challenge(session); provider.putTestChallenge("dns-01", Dns01Challenge::new);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
Authorization auth = new Authorization(session, locationUrl); Authorization auth = new Authorization(session, locationUrl);
auth.update(); auth.update();
@ -122,7 +120,8 @@ public class AuthorizationTest {
assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close(); provider.close();
} }
@ -154,8 +153,8 @@ public class AuthorizationTest {
Session session = provider.createSession(); Session session = provider.createSession();
provider.putTestChallenge("http-01", new Http01Challenge(session)); provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", new Dns01Challenge(session)); provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl); Authorization auth = new Authorization(session, locationUrl);
@ -200,10 +199,8 @@ public class AuthorizationTest {
Session session = provider.createSession(); Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session); provider.putTestChallenge("http-01", Http01Challenge::new);
Dns01Challenge dnsChallenge = new Dns01Challenge(session); provider.putTestChallenge("dns-01", Dns01Challenge::new);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
Authorization auth = new Authorization(session, locationUrl); Authorization auth = new Authorization(session, locationUrl);
@ -220,7 +217,8 @@ public class AuthorizationTest {
assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close(); provider.close();
} }
@ -249,10 +247,8 @@ public class AuthorizationTest {
Session session = provider.createSession(); Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session); provider.putTestChallenge("http-01", Http01Challenge::new);
Dns01Challenge dnsChallenge = new Dns01Challenge(session); provider.putTestChallenge("dns-01", Dns01Challenge::new);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
Authorization auth = new Authorization(session, locationUrl); Authorization auth = new Authorization(session, locationUrl);
auth.deactivate(); auth.deactivate();
@ -267,10 +263,10 @@ public class AuthorizationTest {
try (TestableConnectionProvider provider = new TestableConnectionProvider()) { try (TestableConnectionProvider provider = new TestableConnectionProvider()) {
Session session = provider.createSession(); Session session = provider.createSession();
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session)); provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session)); provider.putTestChallenge(Dns01Challenge.TYPE, Dns01Challenge::new);
provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session)); provider.putTestChallenge(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);
provider.putTestChallenge(DUPLICATE_TYPE, new Challenge(session)); provider.putTestChallenge(DUPLICATE_TYPE, Challenge::new);
Authorization authorization = new Authorization(session, locationUrl); Authorization authorization = new Authorization(session, locationUrl);
authorization.setJSON(getJSON("authorizationChallenges")); authorization.setJSON(getJSON("authorizationChallenges"));

View File

@ -125,9 +125,11 @@ public class SessionTest {
KeyPair keyPair = TestUtils.createKeyPair(); KeyPair keyPair = TestUtils.createKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI); URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
String challengeType = Http01Challenge.TYPE; String challengeType = Http01Challenge.TYPE;
URL challengeUrl = new URL("https://example.com/acme/authz/0");
JSON data = new JSONBuilder() JSON data = new JSONBuilder()
.put("type", challengeType) .put("type", challengeType)
.put("url", challengeUrl)
.toJSON(); .toJSON();
Http01Challenge mockChallenge = mock(Http01Challenge.class); Http01Challenge mockChallenge = mock(Http01Challenge.class);
@ -135,7 +137,7 @@ public class SessionTest {
when(mockProvider.createChallenge( when(mockProvider.createChallenge(
ArgumentMatchers.any(Session.class), ArgumentMatchers.any(Session.class),
ArgumentMatchers.eq(challengeType))) ArgumentMatchers.eq(data)))
.thenReturn(mockChallenge); .thenReturn(mockChallenge);
Session session = new Session(serverUri, keyPair) { Session session = new Session(serverUri, keyPair) {
@ -149,7 +151,7 @@ public class SessionTest {
assertThat(challenge, is(instanceOf(Http01Challenge.class))); assertThat(challenge, is(instanceOf(Http01Challenge.class)));
assertThat(challenge, is(sameInstance((Challenge) mockChallenge))); assertThat(challenge, is(sameInstance((Challenge) mockChallenge)));
verify(mockProvider).createChallenge(session, challengeType); verify(mockProvider).createChallenge(session, data);
} }
/** /**

View File

@ -47,7 +47,6 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/ */
public class ChallengeTest { public class ChallengeTest {
private Session session; private Session session;
private URL resourceUrl = url("https://example.com/acme/some-resource");
private URL locationUrl = url("https://example.com/acme/some-location"); private URL locationUrl = url("https://example.com/acme/some-location");
@Before @Before
@ -74,7 +73,7 @@ public class ChallengeTest {
Session session = provider.createSession(); Session session = provider.createSession();
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session)); provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
Http01Challenge challenge = Challenge.bind(session, locationUrl); Http01Challenge challenge = Challenge.bind(session, locationUrl);
@ -91,8 +90,7 @@ public class ChallengeTest {
*/ */
@Test @Test
public void testUnmarshal() throws URISyntaxException { public void testUnmarshal() throws URISyntaxException {
Challenge challenge = new Challenge(session); Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
challenge.setJSON(getJSON("genericChallenge"));
// Test unmarshalled values // Test unmarshalled values
assertThat(challenge.getType(), is("generic-01")); assertThat(challenge.getType(), is("generic-01"));
@ -126,8 +124,7 @@ public class ChallengeTest {
*/ */
@Test @Test
public void testRespond() throws JoseException { public void testRespond() throws JoseException {
Challenge challenge = new Challenge(session); Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
challenge.setJSON(getJSON("genericChallenge"));
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
challenge.respond(cb); challenge.respond(cb);
@ -140,8 +137,7 @@ public class ChallengeTest {
*/ */
@Test(expected = AcmeProtocolException.class) @Test(expected = AcmeProtocolException.class)
public void testNotAcceptable() throws URISyntaxException { public void testNotAcceptable() throws URISyntaxException {
Http01Challenge challenge = new Http01Challenge(session); new Http01Challenge(session, getJSON("dnsChallenge"));
challenge.setJSON(getJSON("dnsChallenge"));
} }
/** /**
@ -152,7 +148,7 @@ public class ChallengeTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, int... httpStatus) { public int sendSignedRequest(URL url, JSONBuilder claims, Session session, int... httpStatus) {
assertThat(url, is(resourceUrl)); assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("triggerHttpChallengeRequest").toString())); assertThat(claims.toString(), sameJSONAs(getJSON("triggerHttpChallengeRequest").toString()));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(httpStatus, isIntArrayContainingInAnyOrder()); assertThat(httpStatus, isIntArrayContainingInAnyOrder());
@ -167,8 +163,7 @@ public class ChallengeTest {
Session session = provider.createSession(); Session session = provider.createSession();
Http01Challenge challenge = new Http01Challenge(session); Http01Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallenge"));
challenge.setJSON(getJSON("triggerHttpChallenge"));
challenge.trigger(); challenge.trigger();
@ -202,8 +197,7 @@ public class ChallengeTest {
Session session = provider.createSession(); Session session = provider.createSession();
Challenge challenge = new Http01Challenge(session); Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
challenge.setJSON(getJSON("triggerHttpChallengeResponse"));
challenge.update(); challenge.update();
@ -240,8 +234,7 @@ public class ChallengeTest {
Session session = provider.createSession(); Session session = provider.createSession();
Challenge challenge = new Http01Challenge(session); Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
challenge.setJSON(getJSON("triggerHttpChallengeResponse"));
try { try {
challenge.update(); challenge.update();
@ -302,10 +295,9 @@ public class ChallengeTest {
/** /**
* Test that unmarshalling something different like a challenge fails. * Test that unmarshalling something different like a challenge fails.
*/ */
@Test(expected = IllegalArgumentException.class) @Test(expected = AcmeProtocolException.class)
public void testBadUnmarshall() { public void testBadUnmarshall() {
Challenge challenge = new Challenge(session); new Challenge(session, getJSON("updateAccountResponse"));
challenge.setJSON(getJSON("updateAccountResponse"));
} }
} }

View File

@ -46,8 +46,7 @@ public class DnsChallengeTest {
*/ */
@Test @Test
public void testDnsChallenge() throws IOException { public void testDnsChallenge() throws IOException {
Dns01Challenge challenge = new Dns01Challenge(session); Dns01Challenge challenge = new Dns01Challenge(session, getJSON("dnsChallenge"));
challenge.setJSON(getJSON("dnsChallenge"));
assertThat(challenge.getType(), is(Dns01Challenge.TYPE)); assertThat(challenge.getType(), is(Dns01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING)); assertThat(challenge.getStatus(), is(Status.PENDING));

View File

@ -49,8 +49,7 @@ public class HttpChallengeTest {
*/ */
@Test @Test
public void testHttpChallenge() throws IOException { public void testHttpChallenge() throws IOException {
Http01Challenge challenge = new Http01Challenge(session); Http01Challenge challenge = new Http01Challenge(session, getJSON("httpChallenge"));
challenge.setJSON(getJSON("httpChallenge"));
assertThat(challenge.getType(), is(Http01Challenge.TYPE)); assertThat(challenge.getType(), is(Http01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING)); assertThat(challenge.getStatus(), is(Status.PENDING));
@ -69,8 +68,7 @@ public class HttpChallengeTest {
*/ */
@Test(expected = AcmeProtocolException.class) @Test(expected = AcmeProtocolException.class)
public void testNoTokenSet() { public void testNoTokenSet() {
Http01Challenge challenge = new Http01Challenge(session); Http01Challenge challenge = new Http01Challenge(session, getJSON("httpNoTokenChallenge"));
challenge.setJSON(getJSON("httpNoTokenChallenge"));
challenge.getToken(); challenge.getToken();
} }

View File

@ -46,8 +46,7 @@ public class TlsSni02ChallengeTest {
*/ */
@Test @Test
public void testTlsSni02Challenge() throws IOException { public void testTlsSni02Challenge() throws IOException {
TlsSni02Challenge challenge = new TlsSni02Challenge(session); TlsSni02Challenge challenge = new TlsSni02Challenge(session, getJSON("tlsSni02Challenge"));
challenge.setJSON(getJSON("tlsSni02Challenge"));
assertThat(challenge.getType(), is(TlsSni02Challenge.TYPE)); assertThat(challenge.getType(), is(TlsSni02Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING)); assertThat(challenge.getStatus(), is(Status.PENDING));

View File

@ -103,7 +103,7 @@ public class SessionProviderTest {
} }
@Override @Override
public Challenge createChallenge(Session session, String type) { public Challenge createChallenge(Session session, JSON data) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
@ -131,7 +131,7 @@ public class SessionProviderTest {
} }
@Override @Override
public Challenge createChallenge(Session session, String type) { public Challenge createChallenge(Session session, JSON data) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@ -34,7 +34,9 @@ import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection; import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON; import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils; import org.shredzone.acme4j.toolbox.TestUtils;
/** /**
@ -133,29 +135,40 @@ public class AbstractAcmeProviderTest {
} }
}; };
Challenge c1 = provider.createChallenge(session, Http01Challenge.TYPE); Challenge c1 = provider.createChallenge(session, getJSON("httpChallenge"));
assertThat(c1, not(nullValue())); assertThat(c1, not(nullValue()));
assertThat(c1, instanceOf(Http01Challenge.class)); assertThat(c1, instanceOf(Http01Challenge.class));
Challenge c2 = provider.createChallenge(session, Http01Challenge.TYPE); Challenge c2 = provider.createChallenge(session, getJSON("httpChallenge"));
assertThat(c2, not(sameInstance(c1))); assertThat(c2, not(sameInstance(c1)));
Challenge c3 = provider.createChallenge(session, Dns01Challenge.TYPE); Challenge c3 = provider.createChallenge(session, getJSON("dnsChallenge"));
assertThat(c3, not(nullValue())); assertThat(c3, not(nullValue()));
assertThat(c3, instanceOf(Dns01Challenge.class)); assertThat(c3, instanceOf(Dns01Challenge.class));
Challenge c5 = provider.createChallenge(session, TlsSni02Challenge.TYPE); Challenge c5 = provider.createChallenge(session, getJSON("tlsSni02Challenge"));
assertThat(c5, not(nullValue())); assertThat(c5, not(nullValue()));
assertThat(c5, instanceOf(TlsSni02Challenge.class)); assertThat(c5, instanceOf(TlsSni02Challenge.class));
Challenge c6 = provider.createChallenge(session, "foobar-01"); JSON json6 = new JSONBuilder()
.put("type", "foobar-01")
.put("url", "https://example.com/some/challenge")
.toJSON();
Challenge c6 = provider.createChallenge(session, json6);
assertThat(c6, is(nullValue())); assertThat(c6, is(nullValue()));
Challenge c7 = provider.createChallenge(session, ""); try {
assertThat(c7, is(nullValue())); JSON json7 = new JSONBuilder()
.put("url", "https://example.com/some/challenge")
.toJSON();
provider.createChallenge(session, json7);
fail("Challenge without type was accepted");
} catch (AcmeProtocolException ex) {
// expected
}
try { try {
provider.createChallenge(session, (String) null); provider.createChallenge(session, null);
fail("null was accepted"); fail("null was accepted");
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
// expected // expected

View File

@ -18,6 +18,7 @@ import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.BiFunction;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
@ -34,7 +35,8 @@ import org.shredzone.acme4j.toolbox.TestUtils;
* of {@link Connection} that is always returned on {@link #connect()}. * of {@link Connection} that is always returned on {@link #connect()}.
*/ */
public class TestableConnectionProvider extends DummyConnection implements AcmeProvider { public class TestableConnectionProvider extends DummyConnection implements AcmeProvider {
private final Map<String, Challenge> challengeMap = new HashMap<>(); private final Map<String, BiFunction<Session, JSON, Challenge>> creatorMap = new HashMap<>();
private final Map<String, Challenge> createdMap = new HashMap<>();
private final JSONBuilder directory = new JSONBuilder(); private final JSONBuilder directory = new JSONBuilder();
/** /**
@ -50,17 +52,29 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
} }
/** /**
* Register a {@link Challenge}. For the sake of simplicity, * Register a {@link Challenge}.
* {@link #createChallenge(Session, String)} will always return the same
* {@link Challenge} instance in this test suite.
* *
* @param s * @param type
* Challenge type * Challenge type to register.
* @param c * @param creator
* {@link Challenge} instance. * Creator {@link BiFunction} that creates a matching {@link Challenge}
*/ */
public void putTestChallenge(String s, Challenge c) { public void putTestChallenge(String type, BiFunction<Session, JSON, Challenge> creator) {
challengeMap.put(s, c); creatorMap.put(type, creator);
}
/**
* Returns the {@link Challenge} instance that has been created. Fails if no such
* challenge was created.
*
* @param type Challenge type
* @return Created {@link Challenge} instance
*/
public Challenge getChallenge(String type) {
if (!createdMap.containsKey(type)) {
throw new IllegalArgumentException("No challenge of type " + type + " was created");
}
return createdMap.get(type);
} }
/** /**
@ -94,16 +108,23 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
} }
@Override @Override
public Challenge createChallenge(Session session, String type) { public Challenge createChallenge(Session session, JSON data) {
if (challengeMap.isEmpty()) { if (creatorMap.isEmpty()) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
if (challengeMap.containsKey(type)) { Challenge created;
return challengeMap.get(type);
String type = data.get("type").asString();
if (creatorMap.containsKey(type)) {
created = creatorMap.get(type).apply(session, data);
} else { } else {
return new Challenge(session); created = new Challenge(session, data);
} }
createdMap.put(type, created);
return created;
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"type": "http-01", "type": "http-01",
"status": "pending", "status": "pending",
"url": "https://example.com/acme/some-resource", "url": "https://example.com/acme/some-location",
"token": "IlirfxKKXAsHtmzK29Pj8A" "token": "IlirfxKKXAsHtmzK29Pj8A"
} }

View File

@ -37,7 +37,7 @@ The standard Java mechanisms are used to verify the HTTPS certificate provided b
If your ACME server provides challenges that are not specified in the ACME protocol, there should be an own `Challenge` implementation for each of your challenge, by extending the [`Challenge`](../apidocs/org/shredzone/acme4j/challenge/Challenge.html) class. If your ACME server provides challenges that are not specified in the ACME protocol, there should be an own `Challenge` implementation for each of your challenge, by extending the [`Challenge`](../apidocs/org/shredzone/acme4j/challenge/Challenge.html) class.
In your `AcmeProvider` implementation, override the `createChallenge(Session, String)` method so it returns a new instance of your `Challenge` implementation when your individual challenge type is requested. All other types should be delegated to the super method. In your `AcmeProvider` implementation, override the `createChallenge(Session, JSON)` method so it returns a new instance of your `Challenge` implementation when your individual challenge type is requested. All other types should be delegated to the super method.
## Amended Directory Service ## Amended Directory Service