Separate Login from Session

This involves a lot of refactoring and some API changes. However, it allows to clean up some parts of the code that I always considered ugly.
pull/61/head
Richard Körber 2018-02-21 20:01:51 +01:00
parent a111187245
commit dadaf2493f
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
46 changed files with 862 additions and 825 deletions

View File

@ -53,23 +53,8 @@ public class Account extends AcmeJsonResource {
private static final String KEY_CONTACT = "contact";
private static final String KEY_STATUS = "status";
protected Account(Session session, URL location) {
super(session);
setLocation(location);
session.setAccountLocation(location);
}
/**
* Creates a new instance of {@link Account} and binds it to the {@link Session}.
*
* @param session
* {@link Session} to be used
* @param location
* Location URI of the account
* @return {@link Account} bound to the session and location
*/
public static Account bind(Session session, URL location) {
return new Account(session, location);
protected Account(Login login) {
super(login, login.getAccountLocation());
}
/**
@ -112,14 +97,14 @@ public class Account extends AcmeJsonResource {
*/
public Iterator<Order> getOrders() throws AcmeException {
URL ordersUrl = getJSON().get(KEY_ORDERS).asURL();
return new ResourceIterator<>(getSession(), KEY_ORDERS, ordersUrl, Order::bind);
return new ResourceIterator<>(getLogin(), KEY_ORDERS, ordersUrl, Login::bindOrder);
}
@Override
public void update() throws AcmeException {
LOG.debug("update Account");
try (Connection conn = getSession().provider().connect()) {
conn.sendSignedRequest(getLocation(), new JSONBuilder(), getSession());
try (Connection conn = connect()) {
conn.sendSignedRequest(getLocation(), new JSONBuilder(), getLogin());
setJSON(conn.readJsonResponse());
}
}
@ -130,7 +115,7 @@ public class Account extends AcmeJsonResource {
* @return {@link OrderBuilder} object
*/
public OrderBuilder newOrder() throws AcmeException {
return new OrderBuilder(getSession());
return new OrderBuilder(getLogin());
}
/**
@ -161,15 +146,15 @@ public class Account extends AcmeJsonResource {
}
LOG.debug("preAuthorizeDomain {}", domain);
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
claims.object("identifier")
.put("type", "dns")
.put("value", toAce(domain));
conn.sendSignedRequest(newAuthzUrl, claims, getSession());
conn.sendSignedRequest(newAuthzUrl, claims, getLogin());
Authorization auth = new Authorization(getSession(), conn.getLocation());
Authorization auth = getLogin().bindAuthorization(conn.getLocation());
auth.setJSON(conn.readJsonResponse());
return auth;
}
@ -186,14 +171,14 @@ public class Account extends AcmeJsonResource {
*/
public void changeKey(KeyPair newKeyPair) throws AcmeException {
Objects.requireNonNull(newKeyPair, "newKeyPair");
if (Arrays.equals(getSession().getKeyPair().getPrivate().getEncoded(),
if (Arrays.equals(getLogin().getKeyPair().getPrivate().getEncoded(),
newKeyPair.getPrivate().getEncoded())) {
throw new IllegalArgumentException("newKeyPair must actually be a new key pair");
}
LOG.debug("key-change");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
URL keyChangeUrl = getSession().resourceUrl(Resource.KEY_CHANGE);
PublicJsonWebKey newKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(newKeyPair.getPublic());
@ -214,9 +199,9 @@ public class Account extends AcmeJsonResource {
outerClaim.put("signature", innerJws.getEncodedSignature());
outerClaim.put("payload", innerJws.getEncodedPayload());
conn.sendSignedRequest(keyChangeUrl, outerClaim, getSession());
conn.sendSignedRequest(keyChangeUrl, outerClaim, getLogin());
getSession().setKeyPair(newKeyPair);
getLogin().setKeyPair(newKeyPair);
} catch (JoseException ex) {
throw new AcmeProtocolException("Cannot sign key-change", ex);
}
@ -230,11 +215,11 @@ public class Account extends AcmeJsonResource {
*/
public void deactivate() throws AcmeException {
LOG.debug("deactivate");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
claims.put(KEY_STATUS, "deactivated");
conn.sendSignedRequest(getLocation(), claims, getSession());
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
@ -298,13 +283,13 @@ public class Account extends AcmeJsonResource {
*/
public void commit() throws AcmeException {
LOG.debug("modify/commit");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
if (!editContacts.isEmpty()) {
claims.put(KEY_CONTACT, editContacts);
}
conn.sendSignedRequest(getLocation(), claims, getSession());
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}

View File

@ -18,6 +18,7 @@ import static org.shredzone.acme4j.toolbox.AcmeUtils.macKeyAlgorithm;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
@ -50,6 +51,7 @@ public class AccountBuilder {
private Boolean termsOfServiceAgreed;
private Boolean onlyExisting;
private String keyIdentifier;
private KeyPair keyPair;
private SecretKey macKey;
/**
@ -110,6 +112,18 @@ public class AccountBuilder {
return this;
}
/**
* Sets the {@link KeyPair} to be used for this account.
*
* @param keyPair
* Account's {@link KeyPair}
* @return itself
*/
public AccountBuilder useKeyPair(KeyPair keyPair) {
this.keyPair = requireNonNull(keyPair, "keyPair");
return this;
}
/**
* Sets a Key Identifier and MAC key provided by the CA. Use this if your CA requires
* an individual account identification, e.g. your customer number.
@ -120,7 +134,7 @@ public class AccountBuilder {
* MAC key
* @return itself
*/
public AccountBuilder useKeyIdentifier(String kid, SecretKey macKey) {
public AccountBuilder withKeyIdentifier(String kid, SecretKey macKey) {
if (kid != null && kid.isEmpty()) {
throw new IllegalArgumentException("kid must not be empty");
}
@ -139,9 +153,9 @@ public class AccountBuilder {
* Base64url encoded MAC key. It will be decoded for your convenience.
* @return itself
*/
public AccountBuilder useKeyIdentifier(String kid, String encodedMacKey) {
public AccountBuilder withKeyIdentifier(String kid, String encodedMacKey) {
byte[] encodedKey = AcmeUtils.base64UrlDecode(requireNonNull(encodedMacKey, "encodedMacKey"));
return useKeyIdentifier(kid, new HmacKey(encodedKey));
return withKeyIdentifier(kid, new HmacKey(encodedKey));
}
/**
@ -152,12 +166,27 @@ public class AccountBuilder {
* @return {@link Account} referring to the new account
*/
public Account create(Session session) throws AcmeException {
LOG.debug("create");
return createLogin(session).getAccount();
}
if (session.getAccountLocation() != null) {
throw new IllegalArgumentException("session already seems to have an Account");
/**
* Creates a new account.
* <p>
* This method returns a ready to use {@link Login} for the new {@link Account}.
*
* @param session
* {@link Session} to be used for registration
* @return {@link Login} referring to the new account
*/
public Login createLogin(Session session) throws AcmeException {
requireNonNull(session, "session");
if (keyPair == null) {
throw new IllegalStateException("Use AccountBuilder.useKeyPair() to set the account's key pair.");
}
LOG.debug("create");
try (Connection conn = session.provider().connect()) {
URL resourceUrl = session.resourceUrl(Resource.NEW_ACCOUNT);
@ -170,19 +199,19 @@ public class AccountBuilder {
}
if (keyIdentifier != null) {
claims.put("externalAccountBinding", createExternalAccountBinding(
keyIdentifier, session.getKeyPair().getPublic(), macKey, resourceUrl));
keyIdentifier, keyPair.getPublic(), macKey, resourceUrl));
}
if (onlyExisting != null) {
claims.put("onlyReturnExisting", onlyExisting);
}
conn.sendSignedRequest(resourceUrl, claims, session, true);
conn.sendSignedRequest(resourceUrl, claims, session, keyPair);
URL location = conn.getLocation();
Account account = new Account(session, location);
account.setJSON(conn.readJsonResponse());
return account;
Login login = new Login(location, keyPair, session);
login.getAccount().setJSON(conn.readJsonResponse());
return login;
}
}

View File

@ -13,6 +13,7 @@
*/
package org.shredzone.acme4j;
import java.net.URL;
import java.util.Objects;
import org.shredzone.acme4j.connector.Connection;
@ -35,24 +36,13 @@ public abstract class AcmeJsonResource extends AcmeResource {
/**
* Create a new {@link AcmeJsonResource}.
*
* @param session
* {@link Session} the resource is bound with
* @param login
* {@link Login} the resource is bound with
* @param location
* Location {@link URL} of this resource
*/
protected AcmeJsonResource(Session session) {
super(session);
}
/**
* Create a new {@link AcmeJsonResource} and use the given data.
*
* @param session
* {@link Session} the resource is bound with
* @param data
* Initial {@link JSON} data
*/
protected AcmeJsonResource(Session session, JSON data) {
super(session);
setJSON(data);
protected AcmeJsonResource(Login login, URL location) {
super(login, location);
}
/**
@ -122,7 +112,7 @@ public abstract class AcmeJsonResource extends AcmeResource {
public void update() throws AcmeException {
String resourceType = getClass().getSimpleName();
LOG.debug("update {}", resourceType);
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
conn.sendRequest(getLocation(), getSession());
setJSON(conn.readJsonResponse());
conn.handleRetryAfter(resourceType + " is not completed yet");

View File

@ -17,65 +17,67 @@ import java.io.Serializable;
import java.net.URL;
import java.util.Objects;
import org.shredzone.acme4j.connector.Connection;
/**
* A generic ACME resource.
*/
public abstract class AcmeResource implements Serializable {
private static final long serialVersionUID = -7930580802257379731L;
private transient Session session;
private URL location;
private transient Login login;
private final URL location;
/**
* Create a new {@link AcmeResource}.
*
* @param session
* {@link Session} the resource is bound with
* @param login
* {@link Login} the resource is bound with
*/
protected AcmeResource(Session session) {
rebind(session);
protected AcmeResource(Login login, URL location) {
this.location = Objects.requireNonNull(location, "location");
rebind(login);
}
/**
* Gets the {@link Login} this resource is bound with.
*/
protected Login getLogin() {
if (login == null) {
throw new IllegalStateException("Use rebind() for binding this object to a login.");
}
return login;
}
/**
* Gets the {@link Session} this resource is bound with.
*/
protected Session getSession() {
if (session == null) {
throw new IllegalStateException("Use rebind() for binding this object to a session.");
}
return session;
return getLogin().getSession();
}
/**
* Sets a new {@link Session}.
* Opens a {@link Connection} to the provider.
*/
protected void setSession(Session session) {
this.session = Objects.requireNonNull(session, "session");
protected Connection connect() {
return getSession().provider().connect();
}
/**
* Sets the resource's location.
*/
protected void setLocation(URL location) {
this.location = Objects.requireNonNull(location, "location");
}
/**
* Rebinds this resource to a {@link Session}.
* Rebinds this resource to a {@link Login}.
* <p>
* Sessions are not serialized, because they contain volatile session data and also a
* Logins are not serialized, because they contain volatile session data and also a
* private key. After de-serialization of an {@link AcmeResource}, use this method to
* rebind it to a {@link Session}.
* rebind it to a {@link Login}.
*
* @param session
* {@link Session} to bind this resource to
* @param login
* {@link Login} to bind this resource to
*/
public void rebind(Session session) {
if (this.session != null) {
throw new IllegalStateException("Resource is already bound to a session");
public void rebind(Login login) {
if (this.login != null) {
throw new IllegalStateException("Resource is already bound to a login");
}
setSession(session);
this.login = login;
}
/**

View File

@ -38,23 +38,8 @@ public class Authorization extends AcmeJsonResource {
private static final long serialVersionUID = -3116928998379417741L;
private static final Logger LOG = LoggerFactory.getLogger(Authorization.class);
protected Authorization(Session session, URL location) {
super(session);
setLocation(location);
}
/**
* Creates a new instance of {@link Authorization} and binds it to the
* {@link Session}.
*
* @param session
* {@link Session} to be used
* @param location
* Location of the Authorization
* @return {@link Authorization} bound to the session and location
*/
public static Authorization bind(Session session, URL location) {
return new Authorization(session, location);
protected Authorization(Login login, URL location) {
super(login, location);
}
/**
@ -90,13 +75,13 @@ public class Authorization extends AcmeJsonResource {
* Gets a list of all challenges offered by the server.
*/
public List<Challenge> getChallenges() {
Session session = getSession();
Login login = getLogin();
return Collections.unmodifiableList(getJSON().get("challenges")
.asArray()
.stream()
.map(Value::asObject)
.map(session::createChallenge)
.map(login::createChallenge)
.collect(toList()));
}
@ -124,11 +109,11 @@ public class Authorization extends AcmeJsonResource {
*/
public void deactivate() throws AcmeException {
LOG.debug("deactivate");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
claims.put("status", "deactivated");
conn.sendSignedRequest(getLocation(), claims, getSession());
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}

View File

@ -17,7 +17,6 @@ import static java.util.Collections.unmodifiableList;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.CertificateEncodingException;
@ -25,7 +24,6 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.Resource;
@ -47,27 +45,11 @@ public class Certificate extends AcmeResource {
private static final long serialVersionUID = 7381527770159084201L;
private static final Logger LOG = LoggerFactory.getLogger(Certificate.class);
protected static BiFunction<URI, KeyPair, Session> revokeSessionFactory = Session::new;
private ArrayList<X509Certificate> certChain = null;
private ArrayList<URL> alternates = null;
protected Certificate(Session session, URL certUrl) {
super(session);
setLocation(certUrl);
}
/**
* Creates a new instance of {@link Certificate} and binds it to the {@link Session}.
*
* @param session
* {@link Session} to be used
* @param location
* Location of the Certificate
* @return {@link Certificate} bound to the session and location
*/
public static Certificate bind(Session session, URL location) {
return new Certificate(session, location);
protected Certificate(Login login, URL certUrl) {
super(login, certUrl);
}
/**
@ -79,7 +61,7 @@ public class Certificate extends AcmeResource {
public void download() throws AcmeException {
if (certChain == null) {
LOG.debug("download");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
conn.sendRequest(getLocation(), getSession());
alternates = new ArrayList<>(conn.getLinks("alternate"));
certChain = new ArrayList<>(conn.readCertificates());
@ -162,14 +144,14 @@ public class Certificate extends AcmeResource {
throw new AcmeException("Server does not allow certificate revocation");
}
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
claims.putBase64("certificate", getCertificate().getEncoded());
if (reason != null) {
claims.put("reason", reason.getReasonCode());
}
conn.sendSignedRequest(resUrl, claims, getSession(), true);
conn.sendSignedRequest(resUrl, claims, getSession(), getLogin().getKeyPair());
} catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex);
}
@ -179,8 +161,8 @@ public class Certificate extends AcmeResource {
* Revoke a certificate. This call is meant to be used for revoking certificates if
* the account's key pair was lost.
*
* @param serverUri
* {@link URI} of the ACME server
* @param session
* {@link Session} connected to the ACME server
* @param domainKeyPair
* Key pair the CSR was signed with
* @param cert
@ -190,10 +172,9 @@ public class Certificate extends AcmeResource {
* used when generating OCSP responses and CRLs. {@code null} to give no
* reason.
*/
public static void revoke(URI serverUri, KeyPair domainKeyPair, X509Certificate cert,
public static void revoke(Session session, KeyPair domainKeyPair, X509Certificate cert,
RevocationReason reason) throws AcmeException {
LOG.debug("revoke immediately");
Session session = revokeSessionFactory.apply(serverUri, domainKeyPair);
URL resUrl = session.resourceUrl(Resource.REVOKE_CERT);
if (resUrl == null) {
@ -207,7 +188,7 @@ public class Certificate extends AcmeResource {
claims.put("reason", reason.getReasonCode());
}
conn.sendSignedRequest(resUrl, claims, session, true);
conn.sendSignedRequest(resUrl, claims, session, domainKeyPair);
} catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex);
}

View File

@ -0,0 +1,143 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Objects.requireNonNull;
import java.net.URL;
import java.security.KeyPair;
import java.util.Objects;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
/**
* A {@link Login} is a {@link Session} that is connected to an {@link Account} at the
* ACME server. It contains the account's {@link KeyPair} and the {@link URL} of the
* account.
* <p>
* Note that {@link Login} objects are not serializable, as they contain a keypair and
* volatile data.
*/
public class Login {
private final Session session;
private final URL accountLocation;
private final Account account;
private KeyPair keyPair;
/**
* Creates a new {@link Login}.
*
* @param accountLocation
* Account location {@link URL}
* @param keyPair
* {@link KeyPair} of the account
* @param session
* {@link Session} to be used
*/
public Login(URL accountLocation, KeyPair keyPair, Session session) {
this.accountLocation = Objects.requireNonNull(accountLocation, "accountLocation");
this.keyPair = Objects.requireNonNull(keyPair, "keyPair");
this.session = Objects.requireNonNull(session, "session");
this.account = new Account(this);
}
/**
* Gets the {@link Session} that is used.
*/
public Session getSession() {
return session;
}
/**
* Gets the {@link KeyPair} of the ACME account.
*/
public KeyPair getKeyPair() {
return keyPair;
}
/**
* Gets the location {@link URL} of the account.
*/
public URL getAccountLocation() {
return accountLocation;
}
/**
* Gets the {@link Account} that is bound to this login.
*
* @return {@link Account} bound to the login
*/
public Account getAccount() {
return account;
}
/**
* Creates a new instance of {@link Authorization} and binds it to this login.
*
* @param location
* Location of the Authorization
* @return {@link Authorization} bound to the login
*/
public Authorization bindAuthorization(URL location) {
return new Authorization(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of {@link Certificate} and binds it to this login.
*
* @param location
* Location of the Certificate
* @return {@link Certificate} bound to the login
*/
public Certificate bindCertificate(URL location) {
return new Certificate(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of {@link Order} and binds it to this login.
*
* @param location
* Location URL of the order
* @return {@link Order} bound to the login
*/
public Order bindOrder(URL location) {
return new Order(this, requireNonNull(location, "location"));
}
/**
* Creates a {@link Challenge} instance for the given challenge data.
*
* @param data
* Challenge JSON data
* @return {@link Challenge} instance
*/
public Challenge createChallenge(JSON data) {
Challenge challenge = session.provider().createChallenge(this, data);
if (challenge == null) {
throw new AcmeProtocolException("Could not create challenge for: " + data);
}
return challenge;
}
/**
* Sets a different {@link KeyPair}.
*/
protected void setKeyPair(KeyPair keyPair) {
this.keyPair = Objects.requireNonNull(keyPair, "keyPair");
}
}

View File

@ -34,22 +34,8 @@ public class Order extends AcmeJsonResource {
private static final long serialVersionUID = 5435808648658292177L;
private static final Logger LOG = LoggerFactory.getLogger(Order.class);
protected Order(Session session, URL location) {
super(session);
setLocation(location);
}
/**
* Creates a new instance of {@link Order} and binds it to the {@link Session}.
*
* @param session
* {@link Session} to be used
* @param location
* Location URL of the order
* @return {@link Order} bound to the session and location
*/
public static Order bind(Session session, URL location) {
return new Order(session, location);
protected Order(Login login, URL location) {
super(login, location);
}
/**
@ -103,12 +89,12 @@ public class Order extends AcmeJsonResource {
* Gets the {@link Authorization} required for this order.
*/
public List<Authorization> getAuthorizations() {
Session session = getSession();
Login login = getLogin();
return Collections.unmodifiableList(getJSON().get("authorizations")
.asArray()
.stream()
.map(Value::asURL)
.map(loc -> Authorization.bind(session, loc))
.map(login::bindAuthorization)
.collect(toList()));
}
@ -127,7 +113,7 @@ public class Order extends AcmeJsonResource {
public Certificate getCertificate() {
return getJSON().get("certificate").optional()
.map(Value::asURL)
.map(certUrl -> Certificate.bind(getSession(), certUrl))
.map(getLogin()::bindCertificate)
.orElse(null);
}
@ -147,11 +133,11 @@ public class Order extends AcmeJsonResource {
*/
public void execute(byte[] csr) throws AcmeException {
LOG.debug("finalize");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
claims.putBase64("csr", csr);
conn.sendSignedRequest(getFinalizeLocation(), claims, getSession());
conn.sendSignedRequest(getFinalizeLocation(), claims, getLogin());
}
invalidate();
}

View File

@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
public class OrderBuilder {
private static final Logger LOG = LoggerFactory.getLogger(OrderBuilder.class);
private final Session session;
private final Login login;
private final Set<String> domainSet = new LinkedHashSet<>();
private Instant notBefore;
@ -44,10 +44,11 @@ public class OrderBuilder {
/**
* Create a new {@link OrderBuilder}.
*
* @param session {@link Session} to bind with
* @param login
* {@link Login} to bind with
*/
protected OrderBuilder(Session session) {
this.session = session;
protected OrderBuilder(Login login) {
this.login = login;
}
/**
@ -125,6 +126,8 @@ public class OrderBuilder {
throw new IllegalArgumentException("At least one domain is required");
}
Session session = login.getSession();
Object[] identifiers = new Object[domainSet.size()];
Iterator<String> di = domainSet.iterator();
for (int ix = 0; ix < identifiers.length; ix++) {
@ -146,9 +149,9 @@ public class OrderBuilder {
claims.put("notAfter", notAfter);
}
conn.sendSignedRequest(session.resourceUrl(Resource.NEW_ORDER), claims, session);
conn.sendSignedRequest(session.resourceUrl(Resource.NEW_ORDER), claims, login);
Order order = new Order(session, conn.getLocation());
Order order = new Order(login, conn.getLocation());
order.setJSON(conn.readJsonResponse());
return order;
}

View File

@ -26,19 +26,13 @@ import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.StreamSupport;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.toolbox.JSON;
/**
* A session stores the ACME server URI and the account's key pair. It also tracks
* communication parameters.
* <p>
* Note that {@link Session} objects are not serializable, as they contain a keypair and
* volatile data.
* A session stores the ACME server URI. It also tracks communication parameters.
*/
public class Session {
private final AtomicReference<Map<Resource, URL>> resourceMap = new AtomicReference<>();
@ -46,8 +40,6 @@ public class Session {
private final URI serverUri;
private final AcmeProvider provider;
private KeyPair keyPair;
private URL accountLocation;
private byte[] nonce;
private JSON directoryJson;
private Locale locale = Locale.getDefault();
@ -58,11 +50,9 @@ public class Session {
*
* @param serverUri
* URI string of the ACME server
* @param keyPair
* {@link KeyPair} of the ACME account
*/
public Session(String serverUri, KeyPair keyPair) {
this(URI.create(serverUri), keyPair);
public Session(String serverUri) {
this(URI.create(serverUri));
}
/**
@ -70,14 +60,11 @@ public class Session {
*
* @param serverUri
* {@link URI} of the ACME server
* @param keyPair
* {@link KeyPair} of the ACME account
* @throws IllegalArgumentException
* if no ACME provider was found for the server URI.
*/
public Session(URI serverUri, KeyPair keyPair) {
public Session(URI serverUri) {
this.serverUri = Objects.requireNonNull(serverUri, "serverUri");
this.keyPair = Objects.requireNonNull(keyPair, "keyPair");
final URI localServerUri = serverUri;
@ -93,6 +80,19 @@ public class Session {
.orElseThrow(() -> new IllegalArgumentException("No ACME provider found for " + localServerUri));
}
/**
* Logs into an existing account.
*
* @param accountLocation
* Location {@link URL} of the account
* @param accountKeyPair
* Account {@link KeyPair}
* @return {@link Login} to this account
*/
public Login login(URL accountLocation, KeyPair accountKeyPair) {
return new Login(accountLocation, accountKeyPair, this);
}
/**
* Gets the ACME server {@link URI} of this session.
*/
@ -100,34 +100,6 @@ public class Session {
return serverUri;
}
/**
* Gets the {@link KeyPair} of the ACME account.
*/
public KeyPair getKeyPair() {
return keyPair;
}
/**
* Sets a different {@link KeyPair}.
*/
public void setKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
}
/**
* Gets the location {@link URL} of the account logged into this session.
*/
public URL getAccountLocation() {
return accountLocation;
}
/**
* Sets the location {@link URL} of the account logged into this session.
*/
public void setAccountLocation(URL accountLocation) {
this.accountLocation = accountLocation;
}
/**
* Gets the last nonce, or {@code null} if the session is new.
*/
@ -166,21 +138,6 @@ public class Session {
return provider;
}
/**
* Creates a {@link Challenge} instance for the given challenge data.
*
* @param data
* Challenge JSON data
* @return {@link Challenge} instance
*/
public Challenge createChallenge(JSON data) {
Challenge challenge = provider().createChallenge(this, data);
if (challenge == null) {
throw new AcmeProtocolException("Could not create challenge for: " + data);
}
return challenge;
}
/**
* Gets the {@link URL} of the given {@link Resource}. This may involve connecting to
* the server and getting a directory. The result is cached.

View File

@ -13,13 +13,11 @@
*/
package org.shredzone.acme4j.challenge;
import java.net.URL;
import java.time.Instant;
import java.util.Objects;
import org.shredzone.acme4j.AcmeJsonResource;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.exception.AcmeException;
@ -51,40 +49,14 @@ public class Challenge extends AcmeJsonResource {
/**
* Creates a new generic {@link Challenge} object.
*
* @param session
* {@link Session} to bind to.
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Challenge(Session session, JSON data) {
super(session, data);
}
/**
* Returns a {@link Challenge} object of an existing challenge.
*
* @param session
* {@link Session} to be used
* @param location
* Challenge location
* @return {@link Challenge} bound to this session and location
*/
@SuppressWarnings("unchecked")
public static <T extends Challenge> T bind(Session session, URL location) throws AcmeException {
Objects.requireNonNull(session, "session");
Objects.requireNonNull(location, "location");
LOG.debug("bind");
try (Connection conn = session.provider().connect()) {
conn.sendRequest(location, session);
JSON json = conn.readJsonResponse();
if (!(json.contains(KEY_TYPE))) {
throw new IllegalArgumentException("Provided URL is not a challenge URL");
}
return (T) session.createChallenge(json);
}
public Challenge(Login login, JSON data) {
super(login, data.get(KEY_URL).required().asURL());
setJSON(data);
}
/**
@ -146,7 +118,10 @@ public class Challenge extends AcmeJsonResource {
throw new AcmeProtocolException("incompatible type " + type + " for this challenge");
}
setLocation(json.get(KEY_URL).required().asURL());
String loc = json.get(KEY_URL).required().asString();
if (loc != null && !loc.equals(getLocation().toString())) {
throw new AcmeProtocolException("challenge has changed its location");
}
super.setJSON(json);
}
@ -161,11 +136,11 @@ public class Challenge extends AcmeJsonResource {
*/
public void trigger() throws AcmeException {
LOG.debug("trigger");
try (Connection conn = getSession().provider().connect()) {
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
prepareResponse(claims);
conn.sendSignedRequest(getLocation(), claims, getSession());
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}

View File

@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
/**
@ -32,13 +32,13 @@ public class Dns01Challenge extends TokenChallenge {
/**
* Creates a new generic {@link Dns01Challenge} object.
*
* @param session
* {@link Session} to bind to.
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Dns01Challenge(Session session, JSON data) {
super(session, data);
public Dns01Challenge(Login login, JSON data) {
super(login, data);
}
/**

View File

@ -13,7 +13,7 @@
*/
package org.shredzone.acme4j.challenge;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
/**
@ -30,13 +30,13 @@ public class Http01Challenge extends TokenChallenge {
/**
* Creates a new generic {@link Http01Challenge} object.
*
* @param session
* {@link Session} to bind to.
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Http01Challenge(Session session, JSON data) {
super(session, data);
public Http01Challenge(Login login, JSON data) {
super(login, data);
}
/**

View File

@ -19,7 +19,7 @@ import java.security.PublicKey;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
@ -37,13 +37,13 @@ public class TokenChallenge extends Challenge {
/**
* Creates a new generic {@link TokenChallenge} object.
*
* @param session
* {@link Session} to bind to.
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public TokenChallenge(Session session, JSON data) {
super(session, data);
public TokenChallenge(Login login, JSON data) {
super(login, data);
}
@Override
@ -67,7 +67,7 @@ public class TokenChallenge extends Challenge {
*/
protected String getAuthorization() {
try {
PublicKey pk = getSession().getKeyPair().getPublic();
PublicKey pk = getLogin().getKeyPair().getPublic();
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
return getToken()
+ '.'

View File

@ -15,10 +15,12 @@ package org.shredzone.acme4j.connector;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
@ -52,23 +54,26 @@ public interface Connection extends AutoCloseable {
void sendRequest(URL url, Session session) throws AcmeException;
/**
* Sends a signed POST request. Ensures that the session has a KeyIdentifier set, and
* that the "kid" protected header field is used.
* Sends a signed POST request. Requires a {@link Login} for the session and
* {@link KeyPair}. The {@link Login} account location is sent in a "kid" protected
* header.
* <p>
* If the server does not return a 200 class status code, an {@link AcmeException} is
* raised matching the error.
*
* @param url
* {@link URL} to send the request to.
* @param claims
* {@link JSONBuilder} containing claims. Must not be {@code null}.
* @param session
* {@link Session} instance to be used for signing and tracking
* @param login
* {@link Login} instance to be used for signing and tracking.
* @return HTTP 200 class status that was returned
*/
int sendSignedRequest(URL url, JSONBuilder claims, Session session)
throws AcmeException;
int sendSignedRequest(URL url, JSONBuilder claims, Login login) throws AcmeException;
/**
* Sends a signed POST request. If the session's KeyIdentifier is set, a "kid"
* protected header field is sent. If not, a "jwk" protected header field is sent.
* Sends a signed POST request. Only requires a {@link Session}. The {@link KeyPair}
* is sent in a "jwk" protected header field.
* <p>
* If the server does not return a 200 class status code, an {@link AcmeException} is
* raised matching the error.
@ -78,14 +83,12 @@ public interface Connection extends AutoCloseable {
* @param claims
* {@link JSONBuilder} containing claims. Must not be {@code null}.
* @param session
* {@link Session} instance to be used for signing and tracking
* @param enforceJwk
* {@code true} to enforce a "jwk" header field even if a KeyIdentifier is
* set, {@code false} to choose between "kid" and "jwk" header field
* automatically
* {@link Session} instance to be used for tracking.
* @param keypair
* {@link KeyPair} to be used for signing.
* @return HTTP 200 class status that was returned
*/
int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair)
throws AcmeException;
/**

View File

@ -41,6 +41,7 @@ import org.jose4j.base64url.Base64Url;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
@ -153,23 +154,29 @@ public class DefaultConnection implements Connection {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException {
return sendSignedRequest(url, claims, session, false);
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) throws AcmeException {
return sendSignedRequest(url, claims, login.getSession(), login.getKeyPair(), login.getAccountLocation());
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair)
throws AcmeException {
return sendSignedRequest(url, claims, session, keypair, null);
}
private int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair, URL accountLocation)
throws AcmeException {
Objects.requireNonNull(url, "url");
Objects.requireNonNull(claims, "claims");
Objects.requireNonNull(session, "session");
Objects.requireNonNull(keypair, "keypair");
assertConnectionIsClosed();
AcmeException lastException = null;
for (int attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
try {
return performRequest(url, claims, session, enforceJwk);
return performRequest(url, claims, session, keypair, accountLocation);
} catch (AcmeServerException ex) {
if (!BAD_NONCE_ERROR.equals(ex.getType())) {
throw ex;
@ -245,7 +252,7 @@ public class DefaultConnection implements Connection {
String nonceHeader = conn.getHeaderField(REPLAY_NONCE_HEADER);
if (nonceHeader == null || nonceHeader.trim().isEmpty()) {
return null;
return null; //NOSONAR: consistent with other getters
}
if (!BASE64URL_PATTERN.matcher(nonceHeader).matches()) {
@ -291,17 +298,16 @@ public class DefaultConnection implements Connection {
* {@link JSONBuilder} containing claims. Must not be {@code null}.
* @param session
* {@link Session} instance to be used for signing and tracking
* @param enforceJwk
* {@code true} to enforce a "jwk" header field even if a KeyIdentifier is
* set, {@code false} to choose between "kid" and "jwk" header field
* automatically
* @param keypair
* {@link KeyPair} to be used for signing
* @param accountLocation
* If set, the account location is set as "kid" header. If {@code null},
* the public key is set as "jwk" header.
* @return HTTP 200 class status that was returned
*/
private int performRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
private int performRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair, URL accountLocation)
throws AcmeException {
try {
KeyPair keypair = session.getKeyPair();
if (session.getNonce() == null) {
resetNonce(session);
}
@ -319,10 +325,10 @@ public class DefaultConnection implements Connection {
jws.setPayload(claims.toString());
jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce()));
jws.getHeaders().setObjectHeaderValue("url", url);
if (enforceJwk || session.getAccountLocation() == null) {
if (accountLocation == null) {
jws.getHeaders().setJwkHeaderValue("jwk", jwk);
} else {
jws.getHeaders().setObjectHeaderValue("kid", session.getAccountLocation());
jws.getHeaders().setObjectHeaderValue("kid", accountLocation);
}
jws.setAlgorithmHeaderValue(keyAlgorithm(jwk));

View File

@ -22,6 +22,7 @@ import java.util.Objects;
import java.util.function.BiFunction;
import org.shredzone.acme4j.AcmeResource;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
@ -36,28 +37,28 @@ import org.shredzone.acme4j.toolbox.JSON;
*/
public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
private final Session session;
private final Login login;
private final String field;
private final Deque<URL> urlList = new ArrayDeque<>();
private final BiFunction<Session, URL, T> creator;
private final BiFunction<Login, URL, T> creator;
private boolean eol = false;
private URL nextUrl;
/**
* Creates a new {@link ResourceIterator}.
*
* @param session
* {@link Session} to bind this iterator to
* @param login
* {@link Login} to bind this iterator to
* @param field
* Field name to be used in the JSON response
* @param start
* URL of the first JSON array, may be {@code null} for an empty iterator
* @param creator
* Creator for an {@link AcmeResource} that is bound to the given
* {@link Session} and {@link URL}.
* {@link Login} and {@link URL}.
*/
public ResourceIterator(Session session, String field, URL start, BiFunction<Session, URL, T> creator) {
this.session = Objects.requireNonNull(session, "session");
public ResourceIterator(Login login, String field, URL start, BiFunction<Login, URL, T> creator) {
this.login = Objects.requireNonNull(login, "login");
this.field = Objects.requireNonNull(field, "field");
this.nextUrl = start;
this.creator = Objects.requireNonNull(creator, "creator");
@ -106,7 +107,7 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
throw new NoSuchElementException("no more " + field);
}
return creator.apply(session, next);
return creator.apply(login, next);
}
/**
@ -138,6 +139,7 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
* there is a "next" header, it is used for the next batch of URLs.
*/
private void readAndQueue() throws AcmeException {
Session session = login.getSession();
try (Connection conn = session.provider().connect()) {
conn.sendRequest(nextUrl, session);

View File

@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
@ -40,7 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON;
*/
public abstract class AbstractAcmeProvider implements AcmeProvider {
private static final Map<String, BiFunction<Session, JSON, Challenge>> CHALLENGES = challengeMap();
private static final Map<String, BiFunction<Login, JSON, Challenge>> CHALLENGES = challengeMap();
@Override
public Connection connect() {
@ -62,8 +63,8 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
}
}
private static Map<String, BiFunction<Session, JSON, Challenge>> challengeMap() {
Map<String, BiFunction<Session, JSON, Challenge>> map = new HashMap<>();
private static Map<String, BiFunction<Login, JSON, Challenge>> challengeMap() {
Map<String, BiFunction<Login, JSON, Challenge>> map = new HashMap<>();
map.put(Dns01Challenge.TYPE, Dns01Challenge::new);
map.put(Http01Challenge.TYPE, Http01Challenge::new);
@ -81,21 +82,21 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
* are unique to the provider.
*/
@Override
public Challenge createChallenge(Session session, JSON data) {
Objects.requireNonNull(session, "session");
public Challenge createChallenge(Login login, JSON data) {
Objects.requireNonNull(login, "login");
Objects.requireNonNull(data, "data");
String type = data.get("type").required().asString();
BiFunction<Session, JSON, Challenge> constructor = CHALLENGES.get(type);
BiFunction<Login, JSON, Challenge> constructor = CHALLENGES.get(type);
if (constructor != null) {
return constructor.apply(session, data);
return constructor.apply(login, data);
}
if (data.contains("token")) {
return new TokenChallenge(session, data);
return new TokenChallenge(login, data);
} else {
return new Challenge(session, data);
return new Challenge(login, data);
}
}

View File

@ -17,6 +17,7 @@ import java.net.URI;
import java.net.URL;
import java.util.ServiceLoader;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.connector.Connection;
@ -77,13 +78,13 @@ public interface AcmeProvider {
/**
* Creates a {@link Challenge} instance for the given challenge data.
*
* @param session
* {@link Session} to bind the challenge to
* @param login
* {@link Login} to bind the challenge to
* @param data
* Challenge {@link JSON} data
* @return {@link Challenge} instance, or {@code null} if this provider is unable to
* generate a matching {@link Challenge} instance.
*/
Challenge createChallenge(Session session, JSON data);
Challenge createChallenge(Login login, JSON data);
}

View File

@ -20,6 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyPair;
import javax.crypto.SecretKey;
@ -47,12 +48,14 @@ public class AccountBuilderTest {
*/
@Test
public void testRegistration() throws Exception {
KeyPair accountKey = TestUtils.createKeyPair();
TestableConnectionProvider provider = new TestableConnectionProvider() {
private boolean isUpdate;
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(session, is(notNullValue()));
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(login, is(notNullValue()));
assertThat(url, is(locationUrl));
assertThat(isUpdate, is(false));
isUpdate = true;
@ -60,11 +63,11 @@ public class AccountBuilderTest {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
assertThat(session, is(notNullValue()));
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("newAccount").toString()));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(accountKey));
isUpdate = false;
return HttpURLConnection.HTTP_CREATED;
}
@ -85,22 +88,16 @@ public class AccountBuilderTest {
AccountBuilder builder = new AccountBuilder();
builder.addContact("mailto:foo@example.com");
builder.agreeToTermsOfService();
builder.useKeyPair(accountKey);
Session session = provider.createSession();
Account account = builder.create(session);
Login login = builder.createLogin(session);
assertThat(account.getLocation(), is(locationUrl));
assertThat(login.getAccountLocation(), is(locationUrl));
Account account = login.getAccount();
assertThat(account.getTermsOfServiceAgreed(), is(true));
assertThat(session.getAccountLocation(), is(locationUrl));
try {
AccountBuilder builder2 = new AccountBuilder();
builder2.agreeToTermsOfService();
builder2.create(session);
fail("registered twice on same session");
} catch (IllegalArgumentException ex) {
// expected
}
assertThat(account.getLocation(), is(locationUrl));
provider.close();
}
@ -110,16 +107,17 @@ public class AccountBuilderTest {
*/
@Test
public void testRegistrationWithKid() throws Exception {
KeyPair accountKey = TestUtils.createKeyPair();
String keyIdentifier = "NCC-1701";
SecretKey macKey = TestUtils.createSecretKey("SHA-256");
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
try {
assertThat(session, is(notNullValue()));
assertThat(url, is(resourceUrl));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(accountKey));
JSON binding = claims.toJSON()
.get("externalAccountBinding")
@ -170,12 +168,13 @@ public class AccountBuilderTest {
provider.putTestResource(Resource.NEW_ACCOUNT, resourceUrl);
AccountBuilder builder = new AccountBuilder();
builder.useKeyIdentifier(keyIdentifier, AcmeUtils.base64UrlEncode(macKey.getEncoded()));
builder.useKeyPair(accountKey);
builder.withKeyIdentifier(keyIdentifier, AcmeUtils.base64UrlEncode(macKey.getEncoded()));
Session session = provider.createSession();
Account account = builder.create(session);
Login login = builder.createLogin(session);
assertThat(account.getLocation(), is(locationUrl));
assertThat(login.getAccountLocation(), is(locationUrl));
provider.close();
}
@ -185,13 +184,15 @@ public class AccountBuilderTest {
*/
@Test
public void testOnlyExistingRegistration() throws Exception {
KeyPair accountKey = TestUtils.createKeyPair();
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
assertThat(session, is(notNullValue()));
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("newAccountOnlyExisting").toString()));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(accountKey));
return HttpURLConnection.HTTP_OK;
}
@ -209,13 +210,13 @@ public class AccountBuilderTest {
provider.putTestResource(Resource.NEW_ACCOUNT, resourceUrl);
AccountBuilder builder = new AccountBuilder();
builder.useKeyPair(accountKey);
builder.onlyExisting();
Session session = provider.createSession();
Account account = builder.create(session);
Login login = builder.createLogin(session);
assertThat(account.getLocation(), is(locationUrl));
assertThat(session.getAccountLocation(), is(locationUrl));
assertThat(login.getAccountLocation(), is(locationUrl));
provider.close();
}

View File

@ -52,7 +52,7 @@ import org.shredzone.acme4j.toolbox.TestUtils;
public class AccountTest {
private URL resourceUrl = url("http://example.com/acme/resource");
private URL locationUrl = url("http://example.com/acme/account");
private URL locationUrl = url(TestUtils.ACCOUNT_URL);
private URL agreementUrl = url("http://example.com/agreement.pdf");
/**
@ -64,10 +64,10 @@ public class AccountTest {
private JSON jsonResponse;
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("updateAccount").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
jsonResponse = getJSON("updateAccountResponse");
return HttpURLConnection.HTTP_OK;
}
@ -97,11 +97,11 @@ public class AccountTest {
}
};
Session session = provider.createSession();
Account account = new Account(session, locationUrl);
Login login = provider.createLogin();
Account account = new Account(login);
account.update();
assertThat(session.getAccountLocation(), is(locationUrl));
assertThat(login.getAccountLocation(), is(locationUrl));
assertThat(account.getLocation(), is(locationUrl));
assertThat(account.getTermsOfServiceAgreed(), is(true));
assertThat(account.getContacts(), hasSize(1));
@ -125,7 +125,7 @@ public class AccountTest {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
requestWasSent.set(true);
assertThat(url, is(locationUrl));
return HttpURLConnection.HTTP_OK;
@ -150,7 +150,7 @@ public class AccountTest {
}
};
Account account = new Account(provider.createSession(), locationUrl);
Account account = new Account(provider.createLogin());
// Lazy loading
assertThat(requestWasSent.get(), is(false));
@ -173,10 +173,10 @@ public class AccountTest {
public void testPreAuthorizeDomain() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("newAuthorizationRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_CREATED;
}
@ -191,7 +191,7 @@ public class AccountTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl);
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
@ -199,7 +199,7 @@ public class AccountTest {
String domainName = "example.org";
Account account = new Account(session, locationUrl);
Account account = new Account(login);
Authorization auth = account.preAuthorizeDomain(domainName);
assertThat(auth.getDomain(), is(domainName));
@ -224,21 +224,21 @@ public class AccountTest {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) throws AcmeException {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("newAuthorizationRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
Problem problem = TestUtils.createProblem(problemType, problemDetail, resourceUrl);
throw new AcmeServerException(problem);
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl);
Account account = new Account(session, locationUrl);
Account account = new Account(login);
try {
account.preAuthorizeDomain("example.org");
@ -260,8 +260,8 @@ public class AccountTest {
// just provide a resource record so the provider returns a directory
provider.putTestResource(Resource.NEW_NONCE, resourceUrl);
Session session = provider.createSession();
Account account = Account.bind(session, locationUrl);
Login login = provider.createLogin();
Account account = login.getAccount();
try {
account.preAuthorizeDomain(null);
@ -298,11 +298,11 @@ public class AccountTest {
final TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder payload, Session session) {
public int sendSignedRequest(URL url, JSONBuilder payload, Login login) {
try {
assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue()));
assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair)));
assertThat(login, is(notNullValue()));
assertThat(login.getKeyPair(), is(sameInstance(oldKeyPair)));
JSON json = payload.toJSON();
String encodedHeader = json.get("protected").asString();
@ -319,7 +319,7 @@ public class AccountTest {
StringBuilder expectedPayload = new StringBuilder();
expectedPayload.append('{');
expectedPayload.append("\"account\":\"").append(resourceUrl).append("\",");
expectedPayload.append("\"account\":\"").append(locationUrl).append("\",");
expectedPayload.append("\"newKey\":{");
expectedPayload.append("\"kty\":\"").append(TestUtils.D_KTY).append("\",");
expectedPayload.append("\"e\":\"").append(TestUtils.D_E).append("\",");
@ -341,19 +341,21 @@ public class AccountTest {
provider.putTestResource(Resource.KEY_CHANGE, locationUrl);
Session session = new Session(new URI(TestUtils.ACME_SERVER_URI), oldKeyPair) {
Session session = new Session(new URI(TestUtils.ACME_SERVER_URI)) {
@Override
public AcmeProvider provider() {
return provider;
};
};
assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair)));
Login login = new Login(locationUrl, oldKeyPair, session);
Account account = new Account(session, resourceUrl);
assertThat(login.getKeyPair(), is(sameInstance(oldKeyPair)));
Account account = new Account(login);
account.changeKey(newKeyPair);
assertThat(session.getKeyPair(), is(sameInstance(newKeyPair)));
assertThat(login.getKeyPair(), is(sameInstance(newKeyPair)));
}
/**
@ -362,10 +364,10 @@ public class AccountTest {
@Test(expected = IllegalArgumentException.class)
public void testChangeSameKey() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider();
Session session = provider.createSession();
Login login = provider.createLogin();
Account account = new Account(session, locationUrl);
account.changeKey(session.getKeyPair());
Account account = new Account(login);
account.changeKey(login.getKeyPair());
provider.close();
}
@ -377,11 +379,11 @@ public class AccountTest {
public void testDeactivate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
JSON json = claims.toJSON();
assertThat(json.get("status").asString(), is("deactivated"));
assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_OK;
}
@ -391,7 +393,7 @@ public class AccountTest {
}
};
Account account = new Account(provider.createSession(), locationUrl);
Account account = new Account(provider.createLogin());
account.deactivate();
assertThat(account.getStatus(), is(Status.DEACTIVATED));
@ -406,10 +408,10 @@ public class AccountTest {
public void testModify() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("modifyAccount").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_OK;
}
@ -424,7 +426,7 @@ public class AccountTest {
}
};
Account account = new Account(provider.createSession(), locationUrl);
Account account = new Account(provider.createLogin());
account.setJSON(getJSON("newAccount"));
EditableAccount editable = account.modify();

View File

@ -15,7 +15,9 @@ package org.shredzone.acme4j;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
import static org.shredzone.acme4j.toolbox.TestUtils.*;
import java.net.URL;
import org.junit.Test;
import org.shredzone.acme4j.exception.AcmeException;
@ -28,16 +30,19 @@ import org.shredzone.acme4j.toolbox.TestUtils;
public class AcmeJsonResourceTest {
private static final JSON JSON_DATA = getJSON("newAccountResponse");
private static final URL LOCATION_URL = url("https://example.com/acme/resource/123");
/**
* Test {@link AcmeJsonResource#AcmeJsonResource(Session)}.
* Test {@link AcmeJsonResource#AcmeJsonResource(Login, URL)}.
*/
@Test
public void testSessionConstructor() throws Exception {
Session session = TestUtils.session();
public void testLoginConstructor() throws Exception {
Login login = TestUtils.login();
AcmeJsonResource resource = new DummyJsonResource(session);
assertThat(resource.getSession(), is(session));
AcmeJsonResource resource = new DummyJsonResource(login, LOCATION_URL);
assertThat(resource.getLogin(), is(login));
assertThat(resource.getSession(), is(login.getSession()));
assertThat(resource.getLocation(), is(LOCATION_URL));
assertThat(resource.isValid(), is(false));
assertUpdateInvoked(resource, 0);
@ -46,30 +51,16 @@ public class AcmeJsonResourceTest {
assertUpdateInvoked(resource, 1);
}
/**
* Test {@link AcmeJsonResource#AcmeJsonResource(Session, JSON)}.
*/
@Test
public void testSessionAndJsonConstructor() throws Exception {
Session session = TestUtils.session();
AcmeJsonResource resource = new DummyJsonResource(session, JSON_DATA);
assertThat(resource.getSession(), is(session));
assertThat(resource.isValid(), is(true));
assertThat(resource.getJSON(), is(JSON_DATA));
assertUpdateInvoked(resource, 0);
}
/**
* Test {@link AcmeJsonResource#setJSON(JSON)}.
*/
@Test
public void testSetJson() throws Exception {
Session session = TestUtils.session();
Login login = TestUtils.login();
JSON jsonData2 = getJSON("requestOrderResponse");
AcmeJsonResource resource = new DummyJsonResource(session);
AcmeJsonResource resource = new DummyJsonResource(login, LOCATION_URL);
assertThat(resource.isValid(), is(false));
assertUpdateInvoked(resource, 0);
@ -89,13 +80,9 @@ public class AcmeJsonResourceTest {
*/
@Test
public void testInvalidate() throws Exception {
Session session = TestUtils.session();
Login login = TestUtils.login();
AcmeJsonResource resource = new DummyJsonResource(session, JSON_DATA);
assertThat(resource.isValid(), is(true));
assertUpdateInvoked(resource, 0);
resource.invalidate();
AcmeJsonResource resource = new DummyJsonResource(login, LOCATION_URL);
assertThat(resource.isValid(), is(false));
assertUpdateInvoked(resource, 0);
@ -134,12 +121,13 @@ public class AcmeJsonResourceTest {
private int updateCount = 0;
public DummyJsonResource(Session session) {
super(session);
public DummyJsonResource(Login login, URL location) {
super(login, location);
}
public DummyJsonResource(Session session, JSON json) {
super(session, json);
public DummyJsonResource(Login login, URL location, JSON json) {
super(login, location);
setJSON(json);
}
@Override

View File

@ -35,40 +35,19 @@ public class AcmeResourceTest {
*/
@Test
public void testConstructor() throws Exception {
Session session = TestUtils.session();
Login login = TestUtils.login();
URL location = new URL("http://example.com/acme/resource");
try {
new DummyResource(null);
fail("Could create resource without session");
new DummyResource(null, null);
fail("Could create resource without login and location");
} catch (NullPointerException ex) {
// expected
}
AcmeResource resource = new DummyResource(session);
assertThat(resource.getSession(), is(session));
assertThat(resource.getLocation(), is(nullValue()));
resource.setLocation(location);
AcmeResource resource = new DummyResource(login, location);
assertThat(resource.getLogin(), is(login));
assertThat(resource.getLocation(), is(location));
try {
resource.setLocation(null);
fail("Could set location to null");
} catch (NullPointerException ex) {
// expected
}
try {
resource.setSession(null);
fail("Could set session to null");
} catch (NullPointerException ex) {
// expected
}
Session session2 = TestUtils.session();
resource.setSession(session2);
assertThat(resource.getSession(), is(session2));
}
/**
@ -76,11 +55,12 @@ public class AcmeResourceTest {
*/
@Test
public void testSerialization() throws Exception {
Session session = TestUtils.session();
Login login = TestUtils.login();
URL location = new URL("http://example.com/acme/resource");
// Create a Challenge for testing
DummyResource challenge = new DummyResource(session);
assertThat(challenge.getSession(), is(session));
DummyResource challenge = new DummyResource(login, location);
assertThat(challenge.getLogin(), is(login));
// Serialize it
byte[] serialized = null;
@ -107,19 +87,19 @@ public class AcmeResourceTest {
}
assertThat(restored, not(sameInstance(challenge)));
// Make sure the restored object is not attached to a session
// Make sure the restored object is not attached to a login
try {
restored.getSession();
restored.getLogin();
fail("was able to retrieve a session");
} catch (IllegalStateException ex) {
// must fail because we don't have a session in the restored object
// must fail because we don't have a login in the restored object
}
// Set a new session
restored.setSession(session);
// Rebind to login
restored.rebind(login);
// Make sure the new session is set
assertThat(restored.getSession(), is(session));
// Make sure the new login is set
assertThat(restored.getLogin(), is(login));
}
/**
@ -127,12 +107,14 @@ public class AcmeResourceTest {
*/
@Test(expected = IllegalStateException.class)
public void testRebind() throws Exception {
Session session = TestUtils.session();
AcmeResource resource = new DummyResource(session);
assertThat(resource.getSession(), is(session));
Login login = TestUtils.login();
URL location = new URL("http://example.com/acme/resource");
Session session2 = TestUtils.session();
resource.rebind(session2); // fails to rebind to another session
AcmeResource resource = new DummyResource(login, location);
assertThat(resource.getLogin(), is(login));
Login login2 = TestUtils.login();
resource.rebind(login2); // fails to rebind to another login
}
/**
@ -140,8 +122,8 @@ public class AcmeResourceTest {
*/
private static class DummyResource extends AcmeResource {
private static final long serialVersionUID = 7188822681353082472L;
public DummyResource(Session session) {
super(session);
public DummyResource(Login login, URL location) {
super(login, location);
}
}

View File

@ -100,12 +100,12 @@ public class AuthorizationTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
Authorization auth = new Authorization(login, locationUrl);
auth.update();
assertThat(auth.getDomain(), is("example.org"));
@ -145,12 +145,12 @@ public class AuthorizationTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
Authorization auth = new Authorization(login, locationUrl);
// Lazy loading
assertThat(requestWasSent.get(), is(false));
@ -191,12 +191,12 @@ public class AuthorizationTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
Authorization auth = new Authorization(login, locationUrl);
try {
auth.update();
@ -224,11 +224,11 @@ public class AuthorizationTest {
public void testDeactivate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
JSON json = claims.toJSON();
assertThat(json.get("status").asString(), is("deactivated"));
assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_OK;
}
@ -238,12 +238,12 @@ public class AuthorizationTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
Authorization auth = new Authorization(login, locationUrl);
auth.deactivate();
provider.close();
@ -254,13 +254,13 @@ public class AuthorizationTest {
*/
private Authorization createChallengeAuthorization() throws IOException {
try (TestableConnectionProvider provider = new TestableConnectionProvider()) {
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
provider.putTestChallenge(Dns01Challenge.TYPE, Dns01Challenge::new);
provider.putTestChallenge(DUPLICATE_TYPE, Challenge::new);
Authorization authorization = new Authorization(session, locationUrl);
Authorization authorization = new Authorization(login, locationUrl);
authorization.setJSON(getJSON("authorizationChallenges"));
return authorization;
}

View File

@ -23,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
@ -75,7 +74,7 @@ public class CertificateTest {
}
};
Certificate cert = new Certificate(provider.createSession(), locationUrl);
Certificate cert = new Certificate(provider.createLogin(), locationUrl);
cert.download();
X509Certificate downloadedCert = cert.getCertificate();
@ -132,12 +131,11 @@ public class CertificateTest {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(session.getAccountLocation(), is(nullValue()));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(notNullValue()));
certRequested = false;
return HttpURLConnection.HTTP_OK;
}
@ -157,7 +155,7 @@ public class CertificateTest {
provider.putTestResource(Resource.REVOKE_CERT, resourceUrl);
Certificate cert = new Certificate(provider.createSession(), locationUrl);
Certificate cert = new Certificate(provider.createLogin(), locationUrl);
cert.revoke();
provider.close();
@ -181,11 +179,11 @@ public class CertificateTest {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateWithReasonRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(notNullValue()));
certRequested = false;
return HttpURLConnection.HTTP_OK;
}
@ -205,7 +203,7 @@ public class CertificateTest {
provider.putTestResource(Resource.REVOKE_CERT, resourceUrl);
Certificate cert = new Certificate(provider.createSession(), locationUrl);
Certificate cert = new Certificate(provider.createLogin(), locationUrl);
cert.revoke(RevocationReason.KEY_COMPROMISE);
provider.close();
@ -223,20 +221,17 @@ public class CertificateTest {
* Test that a certificate can be revoked by its domain key pair.
*/
@Test
@SuppressWarnings("resource")
public void testRevokeCertificateByKeyPair() throws AcmeException, IOException {
final List<X509Certificate> originalCert = TestUtils.createCertificate();
final KeyPair certKeyPair = TestUtils.createDomainKeyPair();
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
throws AcmeException {
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair) {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateWithReasonRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(session.getKeyPair(), is(certKeyPair));
assertThat(enforceJwk, is(true));
assertThat(keypair, is(certKeyPair));
return HttpURLConnection.HTTP_OK;
}
};
@ -244,15 +239,8 @@ public class CertificateTest {
provider.putTestResource(Resource.REVOKE_CERT, resourceUrl);
Session session = provider.createSession();
URI serverUri = session.getServerUri();
Certificate.revokeSessionFactory = (uri, keyPair) -> {
assertThat(uri, is(serverUri));
session.setKeyPair(keyPair);
return session;
};
Certificate.revoke(serverUri, certKeyPair, originalCert.get(0), RevocationReason.KEY_COMPROMISE);
Certificate.revoke(session, certKeyPair, originalCert.get(0), RevocationReason.KEY_COMPROMISE);
provider.close();
}

View File

@ -0,0 +1,138 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import static org.shredzone.acme4j.toolbox.TestUtils.url;
import java.io.IOException;
import java.net.URL;
import java.security.KeyPair;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link Login}.
*/
public class LoginTest {
private static final URL resourceUrl = url("https://example.com/acme/resource/123");
/**
* Test the constructor.
*/
@Test
public void testConstructor() throws IOException {
URL location = url(TestUtils.ACCOUNT_URL);
KeyPair keypair = TestUtils.createKeyPair();
Session session = TestUtils.session();
Login login = new Login(location, keypair, session);
assertThat(login.getAccountLocation(), is(location));
assertThat(login.getKeyPair(), is(keypair));
assertThat(login.getSession(), is(session));
assertThat(login.getAccount(), is(notNullValue()));
assertThat(login.getAccount().getLogin(), is(login));
assertThat(login.getAccount().getLocation(), is(location));
assertThat(login.getAccount().getSession(), is(session));
}
/**
* Test the simple binders.
*/
@Test
public void testBinder() throws IOException {
URL location = url(TestUtils.ACCOUNT_URL);
KeyPair keypair = TestUtils.createKeyPair();
Session session = TestUtils.session();
Login login = new Login(location, keypair, session);
Authorization auth = login.bindAuthorization(resourceUrl);
assertThat(auth, is(notNullValue()));
assertThat(auth.getLogin(), is(login));
assertThat(auth.getLocation(), is(resourceUrl));
Certificate cert = login.bindCertificate(resourceUrl);
assertThat(cert, is(notNullValue()));
assertThat(cert.getLogin(), is(login));
assertThat(cert.getLocation(), is(resourceUrl));
Order order = login.bindOrder(resourceUrl);
assertThat(order, is(notNullValue()));
assertThat(order.getLogin(), is(login));
assertThat(order.getLocation(), is(resourceUrl));
}
/**
* Test that the account's keypair can be changed.
*/
@Test
public void testKeyChange() throws IOException {
URL location = url(TestUtils.ACCOUNT_URL);
KeyPair keypair = TestUtils.createKeyPair();
Session session = TestUtils.session();
Login login = new Login(location, keypair, session);
assertThat(login.getKeyPair(), is(keypair));
KeyPair keypair2 = TestUtils.createKeyPair();
login.setKeyPair(keypair2);
assertThat(login.getKeyPair(), is(keypair2));
}
/**
* Test that challenges are correctly created via provider.
*/
@Test
public void testCreateChallenge() throws Exception {
String challengeType = Http01Challenge.TYPE;
URL challengeUrl = url("https://example.com/acme/authz/0");
JSON data = new JSONBuilder()
.put("type", challengeType)
.put("url", challengeUrl)
.toJSON();
Http01Challenge mockChallenge = mock(Http01Challenge.class);
final AcmeProvider mockProvider = mock(AcmeProvider.class);
when(mockProvider.createChallenge(
ArgumentMatchers.any(Login.class),
ArgumentMatchers.eq(data)))
.thenReturn(mockChallenge);
URL location = url(TestUtils.ACCOUNT_URL);
KeyPair keypair = TestUtils.createKeyPair();
Session session = TestUtils.session(mockProvider);
Login login = new Login(location, keypair, session);
Challenge challenge = login.createChallenge(data);
assertThat(challenge, is(instanceOf(Http01Challenge.class)));
assertThat(challenge, is(sameInstance((Challenge) mockChallenge)));
verify(mockProvider).createChallenge(login, data);
}
}

View File

@ -29,6 +29,7 @@ import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.provider.TestableConnectionProvider;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link OrderBuilder}.
@ -36,7 +37,7 @@ import org.shredzone.acme4j.toolbox.JSONBuilder;
public class OrderBuilderTest {
private URL resourceUrl = url("http://example.com/acme/resource");
private URL locationUrl = url("http://example.com/acme/account");
private URL locationUrl = url(TestUtils.ACCOUNT_URL);
/**
* Test that a new {@link Order} can be created.
@ -48,10 +49,10 @@ public class OrderBuilderTest {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("requestOrderRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_CREATED;
}
@ -66,11 +67,11 @@ public class OrderBuilderTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
Account account = new Account(session, locationUrl);
Account account = new Account(login);
Order order = account.newOrder()
.domains("example.com", "www.example.com")
.domain("example.org")

View File

@ -62,9 +62,9 @@ public class OrderTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Order order = new Order(session, locationUrl);
Order order = new Order(login, locationUrl);
order.update();
assertThat(order.getStatus(), is(Status.PENDING));
@ -116,9 +116,9 @@ public class OrderTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Order order = new Order(session, locationUrl);
Order order = new Order(login, locationUrl);
// Lazy loading
assertThat(requestWasSent.get(), is(false));
@ -151,10 +151,10 @@ public class OrderTest {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(finalizeUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("finalizeRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
isFinalized = true;
return HttpURLConnection.HTTP_OK;
}
@ -170,9 +170,9 @@ public class OrderTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Order order = new Order(session, locationUrl);
Order order = new Order(login, locationUrl);
order.execute(csr);
assertThat(order.getStatus(), is(Status.VALID));

View File

@ -26,13 +26,9 @@ import java.time.Instant;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
@ -45,44 +41,25 @@ public class SessionTest {
*/
@Test
public void testConstructor() throws IOException {
KeyPair keyPair = TestUtils.createKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
try {
new Session((URI) null, null);
new Session((URI) null);
fail("accepted null parameters in constructor");
} catch (NullPointerException ex) {
// expected
}
try {
new Session(serverUri, null);
fail("accepted null parameters in constructor");
} catch (NullPointerException ex) {
// expected
}
try {
new Session((URI) null, keyPair);
fail("accepted null parameters in constructor");
} catch (NullPointerException ex) {
// expected
}
Session session = new Session(serverUri, keyPair);
Session session = new Session(serverUri);
assertThat(session, not(nullValue()));
assertThat(session.getServerUri(), is(serverUri));
assertThat(session.getKeyPair(), is(keyPair));
assertThat(session.getAccountLocation(), is(nullValue()));
Session session2 = new Session("https://example.com/acme", keyPair);
Session session2 = new Session(TestUtils.ACME_SERVER_URI);
assertThat(session2, not(nullValue()));
assertThat(session2.getServerUri(), is(serverUri));
assertThat(session2.getKeyPair(), is(keyPair));
assertThat(session2.getAccountLocation(), is(nullValue()));
try {
new Session("#*aBaDuRi*#", keyPair);
new Session("#*aBaDuRi*#");
fail("accepted bad URI in constructor");
} catch (IllegalArgumentException ex) {
// expected
@ -94,64 +71,34 @@ public class SessionTest {
*/
@Test
public void testGettersAndSetters() throws IOException {
KeyPair kp1 = TestUtils.createKeyPair();
KeyPair kp2 = TestUtils.createDomainKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
URL accountUrl = TestUtils.url(TestUtils.ACME_SERVER_URI + "/acct/1");
Session session = new Session(serverUri, kp1);
Session session = new Session(serverUri);
assertThat(session.getNonce(), is(nullValue()));
byte[] data = "foo-nonce-bar".getBytes();
session.setNonce(data);
assertThat(session.getNonce(), is(equalTo(data)));
assertThat(session.getKeyPair(), is(kp1));
session.setKeyPair(kp2);
assertThat(session.getKeyPair(), is(kp2));
assertThat(session.getAccountLocation(), is(nullValue()));
session.setAccountLocation(accountUrl);
assertThat(session.getAccountLocation(), is(accountUrl));
assertThat(session.getServerUri(), is(serverUri));
}
/**
* Test if challenges are correctly created via provider.
* Test login methods.
*/
@Test
public void testCreateChallenge() throws IOException {
KeyPair keyPair = TestUtils.createKeyPair();
public void testLogin() throws IOException {
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
String challengeType = Http01Challenge.TYPE;
URL challengeUrl = new URL("https://example.com/acme/authz/0");
URL accountLocation = url(TestUtils.ACCOUNT_URL);
KeyPair accountKeyPair = TestUtils.createKeyPair();
JSON data = new JSONBuilder()
.put("type", challengeType)
.put("url", challengeUrl)
.toJSON();
Session session = new Session(serverUri);
Http01Challenge mockChallenge = mock(Http01Challenge.class);
final AcmeProvider mockProvider = mock(AcmeProvider.class);
when(mockProvider.createChallenge(
ArgumentMatchers.any(Session.class),
ArgumentMatchers.eq(data)))
.thenReturn(mockChallenge);
Session session = new Session(serverUri, keyPair) {
@Override
public AcmeProvider provider() {
return mockProvider;
};
};
Challenge challenge = session.createChallenge(data);
assertThat(challenge, is(instanceOf(Http01Challenge.class)));
assertThat(challenge, is(sameInstance((Challenge) mockChallenge)));
verify(mockProvider).createChallenge(session, data);
Login login = session.login(accountLocation, accountKeyPair);
assertThat(login, is(notNullValue()));
assertThat(login.getSession(), is(session));
assertThat(login.getAccountLocation(), is(accountLocation));
assertThat(login.getKeyPair(), is(accountKeyPair));
}
/**
@ -159,7 +106,6 @@ public class SessionTest {
*/
@Test
public void testDirectory() throws AcmeException, IOException {
KeyPair keyPair = TestUtils.createKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
final AcmeProvider mockProvider = mock(AcmeProvider.class);
@ -168,7 +114,7 @@ public class SessionTest {
ArgumentMatchers.eq(serverUri)))
.thenReturn(getJSON("directory"));
Session session = new Session(serverUri, keyPair) {
Session session = new Session(serverUri) {
@Override
public AcmeProvider provider() {
return mockProvider;
@ -197,7 +143,6 @@ public class SessionTest {
*/
@Test
public void testNoMeta() throws AcmeException, IOException {
KeyPair keyPair = TestUtils.createKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
final AcmeProvider mockProvider = mock(AcmeProvider.class);
@ -206,7 +151,7 @@ public class SessionTest {
ArgumentMatchers.eq(serverUri)))
.thenReturn(getJSON("directoryNoMeta"));
Session session = new Session(serverUri, keyPair) {
Session session = new Session(serverUri) {
@Override
public AcmeProvider provider() {
return mockProvider;

View File

@ -19,7 +19,6 @@ import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp;
import static org.shredzone.acme4j.toolbox.TestUtils.*;
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
@ -28,8 +27,8 @@ import java.time.Duration;
import java.time.Instant;
import org.jose4j.lang.JoseException;
import org.junit.Before;
import org.junit.Test;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
@ -45,51 +44,14 @@ import org.shredzone.acme4j.toolbox.TestUtils;
* Unit tests for {@link Challenge}.
*/
public class ChallengeTest {
private Session session;
private URL locationUrl = url("https://example.com/acme/some-location");
@Before
public void setup() throws IOException {
session = TestUtils.session();
}
/**
* Test that a challenge is properly restored.
*/
@Test
public void testChallenge() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public void sendRequest(URL url, Session session) {
assertThat(url, is(locationUrl));
}
@Override
public JSON readJsonResponse() {
return getJSON("updateHttpChallengeResponse");
}
};
Session session = provider.createSession();
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
Http01Challenge challenge = Challenge.bind(session, locationUrl);
assertThat(challenge.getType(), is(Http01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.VALID));
assertThat(challenge.getLocation(), is(locationUrl));
assertThat(challenge.getToken(), is("IlirfxKKXAsHtmzK29Pj8A"));
provider.close();
}
/**
* Test that after unmarshaling, the challenge properties are set correctly.
*/
@Test
public void testUnmarshal() throws URISyntaxException {
Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
Challenge challenge = new Challenge(TestUtils.login(), getJSON("genericChallenge"));
// Test unmarshalled values
assertThat(challenge.getType(), is("generic-01"));
@ -113,7 +75,7 @@ public class ChallengeTest {
*/
@Test
public void testRespond() throws JoseException {
Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
Challenge challenge = new Challenge(TestUtils.login(), getJSON("genericChallenge"));
JSONBuilder response = new JSONBuilder();
challenge.prepareResponse(response);
@ -126,7 +88,7 @@ public class ChallengeTest {
*/
@Test(expected = AcmeProtocolException.class)
public void testNotAcceptable() throws URISyntaxException {
new Http01Challenge(session, getJSON("dnsChallenge"));
new Http01Challenge(TestUtils.login(), getJSON("dnsChallenge"));
}
/**
@ -136,10 +98,10 @@ public class ChallengeTest {
public void testTrigger() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session) {
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("triggerHttpChallengeRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(login, is(notNullValue()));
return HttpURLConnection.HTTP_OK;
}
@ -149,9 +111,9 @@ public class ChallengeTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Http01Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallenge"));
Http01Challenge challenge = new Http01Challenge(login, getJSON("triggerHttpChallenge"));
challenge.trigger();
@ -183,9 +145,9 @@ public class ChallengeTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
Challenge challenge = new Http01Challenge(login, getJSON("triggerHttpChallengeResponse"));
challenge.update();
@ -220,9 +182,9 @@ public class ChallengeTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
Challenge challenge = new Http01Challenge(login, getJSON("triggerHttpChallengeResponse"));
try {
challenge.update();
@ -237,55 +199,12 @@ public class ChallengeTest {
provider.close();
}
/**
* Test that null is handled properly.
*/
@Test
public void testNullChallenge() throws Exception {
try {
Challenge.bind(session, null);
fail("locationUri accepts null");
} catch (NullPointerException ex) {
// expected
}
try {
Challenge.bind(null, locationUrl);
fail("session accepts null");
} catch (NullPointerException ex) {
// expected
}
}
/**
* Test that an exception is thrown on a bad location URL.
*/
@Test(expected = IllegalArgumentException.class)
public void testBadBind() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public void sendRequest(URL url, Session session) {
assertThat(url, is(locationUrl));
}
@Override
public JSON readJsonResponse() {
return getJSON("updateAccountResponse");
}
};
Session session = provider.createSession();
Challenge.bind(session, locationUrl);
provider.close();
}
/**
* Test that unmarshalling something different like a challenge fails.
*/
@Test(expected = AcmeProtocolException.class)
public void testBadUnmarshall() {
new Challenge(session, getJSON("updateAccountResponse"));
new Challenge(TestUtils.login(), getJSON("updateAccountResponse"));
}
}

View File

@ -20,9 +20,8 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
@ -34,19 +33,14 @@ public class DnsChallengeTest {
private static final String KEY_AUTHORIZATION =
"pNvmJivs0WCko2suV7fhe-59oFqyYx_yB7tx6kIMAyE.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0";
private static Session session;
@BeforeClass
public static void setup() throws IOException {
session = TestUtils.session();
}
private Login login = TestUtils.login();
/**
* Test that {@link Dns01Challenge} generates a correct authorization key.
*/
@Test
public void testDnsChallenge() throws IOException {
Dns01Challenge challenge = new Dns01Challenge(session, getJSON("dnsChallenge"));
Dns01Challenge challenge = new Dns01Challenge(login, getJSON("dnsChallenge"));
assertThat(challenge.getType(), is(Dns01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));

View File

@ -20,9 +20,8 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSONBuilder;
@ -37,19 +36,14 @@ public class HttpChallengeTest {
private static final String KEY_AUTHORIZATION =
"rSoI9JpyvFi-ltdnBW0W1DjKstzG7cHixjzcOjwzAEQ.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0";
private static Session session;
@BeforeClass
public static void setup() throws IOException {
session = TestUtils.session();
}
private Login login = TestUtils.login();
/**
* Test that {@link Http01Challenge} generates a correct authorization key.
*/
@Test
public void testHttpChallenge() throws IOException {
Http01Challenge challenge = new Http01Challenge(session, getJSON("httpChallenge"));
Http01Challenge challenge = new Http01Challenge(login, getJSON("httpChallenge"));
assertThat(challenge.getType(), is(Http01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));
@ -68,7 +62,7 @@ public class HttpChallengeTest {
*/
@Test(expected = AcmeProtocolException.class)
public void testNoTokenSet() {
Http01Challenge challenge = new Http01Challenge(session, getJSON("httpNoTokenChallenge"));
Http01Challenge challenge = new Http01Challenge(login, getJSON("httpNoTokenChallenge"));
challenge.getToken();
}

View File

@ -26,6 +26,7 @@ import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
@ -43,6 +44,7 @@ import org.jose4j.jwx.CompactSerializer;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeNetworkException;
@ -64,10 +66,12 @@ import org.shredzone.acme4j.toolbox.TestUtils;
public class DefaultConnectionTest {
private URL requestUrl = TestUtils.url("http://example.com/acme/");
private URL accountUrl = TestUtils.url(TestUtils.ACME_SERVER_URI + "/acct/1");
private URL accountUrl = TestUtils.url(TestUtils.ACCOUNT_URL);
private HttpURLConnection mockUrlConnection;
private HttpConnector mockHttpConnection;
private Session session;
private Login login;
private KeyPair keyPair;
@Before
public void setup() throws AcmeException, IOException {
@ -84,11 +88,15 @@ public class DefaultConnectionTest {
session = TestUtils.session(mockProvider);
session.setLocale(Locale.JAPAN);
keyPair = TestUtils.createKeyPair();
login = session.login(accountUrl, keyPair);
}
/**
* Test if {@link DefaultConnection#updateSession(Session)} does nothing if there is
* no {@code Replay-Nonce} header.
* Test that {@link DefaultConnection#getNonce()} returns {@code null} if there is no
* {@code Replay-Nonce} header.
*/
@Test
public void testNoNonceFromHeader() throws AcmeException {
@ -105,8 +113,8 @@ public class DefaultConnectionTest {
}
/**
* Test that {@link DefaultConnection#updateSession(Session)} extracts a
* {@code Replay-Nonce} header correctly.
* Test that {@link DefaultConnection#getNonce()} extracts a {@code Replay-Nonce}
* header correctly.
*/
@Test
public void testGetNonceFromHeader() {
@ -123,7 +131,7 @@ public class DefaultConnectionTest {
}
/**
* Test that {@link DefaultConnection#updateSession(Session)} fails on an invalid
* Test that {@link DefaultConnection#getNonce()} fails on an invalid
* {@code Replay-Nonce} header.
*/
@Test
@ -400,11 +408,10 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(mockUrlConnection.getOutputStream()).thenReturn(new ByteArrayOutputStream());
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
int rc = conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
int rc = conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
assertThat(rc, is(HttpURLConnection.HTTP_OK));
}
@ -424,11 +431,10 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
when(mockUrlConnection.getURL()).thenReturn(url("https://example.com/acme/1"));
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeUnauthorizedException ex) {
assertThat(ex.getType(), is(URI.create("urn:ietf:params:acme:error:unauthorized")));
@ -460,11 +466,10 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
when(mockUrlConnection.getURL()).thenReturn(url("https://example.com/acme/1"));
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeUserActionRequiredException ex) {
assertThat(ex.getType(), is(URI.create("urn:ietf:params:acme:error:userActionRequired")));
@ -502,11 +507,10 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
when(mockUrlConnection.getURL()).thenReturn(url("https://example.com/acme/1"));
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeRateLimitedException ex) {
assertThat(ex.getType(), is(URI.create("urn:ietf:params:acme:error:rateLimited")));
@ -542,7 +546,6 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getOutputStream())
.thenReturn(new ByteArrayOutputStream());
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
@ -554,7 +557,7 @@ public class DefaultConnectionTest {
return result.toJSON();
};
}) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeServerException ex) {
assertThat(ex.getType(), is(URI.create("urn:zombie:error:apocalypse")));
@ -582,7 +585,6 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getOutputStream())
.thenReturn(new ByteArrayOutputStream());
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
@ -591,7 +593,7 @@ public class DefaultConnectionTest {
return JSON.empty();
};
}) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeNetworkException ex) {
fail("Did not expect an AcmeNetworkException");
@ -618,11 +620,10 @@ public class DefaultConnectionTest {
when(mockUrlConnection.getOutputStream())
.thenReturn(new ByteArrayOutputStream());
session.setAccountLocation(accountUrl);
session.setNonce(TestUtils.DUMMY_NONCE);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.sendSignedRequest(requestUrl, new JSONBuilder(), session);
conn.sendSignedRequest(requestUrl, new JSONBuilder(), login);
fail("Expected to fail");
} catch (AcmeException ex) {
assertThat(ex.getMessage(), is("HTTP 500: Infernal Server Error"));
@ -690,8 +691,7 @@ public class DefaultConnectionTest {
}) {
JSONBuilder cb = new JSONBuilder();
cb.put("foo", 123).put("bar", "a-string");
session.setAccountLocation(accountUrl);
conn.sendSignedRequest(requestUrl, cb, session);
conn.sendSignedRequest(requestUrl, cb, login);
}
verify(mockUrlConnection).setRequestMethod("POST");
@ -726,7 +726,7 @@ public class DefaultConnectionTest {
JsonWebSignature jws = new JsonWebSignature();
jws.setCompactSerialization(CompactSerializer.serialize(encodedHeader, encodedPayload, encodedSignature));
jws.setKey(session.getKeyPair().getPublic());
jws.setKey(login.getKeyPair().getPublic());
assertThat(jws.verifySignature(), is(true));
}
@ -766,7 +766,7 @@ public class DefaultConnectionTest {
}) {
JSONBuilder cb = new JSONBuilder();
cb.put("foo", 123).put("bar", "a-string");
conn.sendSignedRequest(requestUrl, cb, session, true);
conn.sendSignedRequest(requestUrl, cb, session, keyPair);
}
verify(mockUrlConnection).setRequestMethod("POST");
@ -804,7 +804,7 @@ public class DefaultConnectionTest {
JsonWebSignature jws = new JsonWebSignature();
jws.setCompactSerialization(CompactSerializer.serialize(encodedHeader, encodedPayload, encodedSignature));
jws.setKey(session.getKeyPair().getPublic());
jws.setKey(login.getKeyPair().getPublic());
assertThat(jws.verifySignature(), is(true));
}
@ -820,7 +820,7 @@ public class DefaultConnectionTest {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
JSONBuilder cb = new JSONBuilder();
conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session, true);
conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session, DefaultConnectionTest.this.keyPair);
}
}

View File

@ -14,10 +14,12 @@
package org.shredzone.acme4j.connector;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.toolbox.JSON;
@ -40,13 +42,13 @@ public class DummyConnection implements Connection {
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session)
public int sendSignedRequest(URL url, JSONBuilder claims, Login login)
throws AcmeException {
throw new UnsupportedOperationException();
}
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, KeyPair keypair)
throws AcmeException {
throw new UnsupportedOperationException();
}

View File

@ -30,6 +30,7 @@ import java.util.NoSuchElementException;
import org.junit.Before;
import org.junit.Test;
import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.provider.TestableConnectionProvider;
import org.shredzone.acme4j.toolbox.JSON;
@ -158,11 +159,11 @@ public class ResourceIteratorTest {
}
};
Session session = provider.createSession();
Login login = provider.createLogin();
provider.close();
return new ResourceIterator<>(session, TYPE, first, Authorization::bind);
return new ResourceIterator<>(login, TYPE, first, Login::bindAuthorization);
}
}

View File

@ -16,20 +16,17 @@ package org.shredzone.acme4j.connector;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.util.ServiceLoader;
import org.junit.Before;
import org.junit.Test;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link Session#provider()}. Requires that both enclosed
@ -38,20 +35,13 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/
public class SessionProviderTest {
private KeyPair keyPair;
@Before
public void setup() throws IOException {
keyPair = TestUtils.createKeyPair();
}
/**
* There are no testing providers accepting {@code acme://example.org}. Test that
* connecting to this URI will result in an {@link IllegalArgumentException}.
*/
@Test(expected = IllegalArgumentException.class)
public void testNone() throws Exception {
new Session(new URI("acme://example.org"), keyPair).provider();
new Session(new URI("acme://example.org")).provider();
}
/**
@ -60,7 +50,7 @@ public class SessionProviderTest {
*/
@Test
public void testConnectURI() throws Exception {
Session session = new Session(new URI("acme://example.com"), keyPair);
Session session = new Session(new URI("acme://example.com"));
AcmeProvider provider = session.provider();
assertThat(provider, is(instanceOf(Provider1.class)));
@ -76,7 +66,7 @@ public class SessionProviderTest {
*/
@Test(expected = IllegalArgumentException.class)
public void testDuplicate() throws Exception {
new Session(new URI("acme://example.net"), keyPair).provider();
new Session(new URI("acme://example.net")).provider();
}
public static class Provider1 implements AcmeProvider {
@ -103,7 +93,7 @@ public class SessionProviderTest {
}
@Override
public Challenge createChallenge(Session session, JSON data) {
public Challenge createChallenge(Login login, JSON data) {
throw new UnsupportedOperationException();
}
}
@ -131,7 +121,7 @@ public class SessionProviderTest {
}
@Override
public Challenge createChallenge(Session session, JSON data) {
public Challenge createChallenge(Login login, JSON data) {
throw new UnsupportedOperationException();
}
}

View File

@ -21,7 +21,7 @@ import java.net.URL;
import org.junit.Test;
import org.shredzone.acme4j.AcmeResource;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
@ -33,8 +33,8 @@ public class AcmeLazyLoadingExceptionTest {
@Test
public void testAcmeLazyLoadingException() {
Session session = mock(Session.class);
AcmeResource resource = new TestResource(session, resourceUrl);
Login login = mock(Login.class);
AcmeResource resource = new TestResource(login, resourceUrl);
AcmeException cause = new AcmeException("Something went wrong");
@ -50,9 +50,8 @@ public class AcmeLazyLoadingExceptionTest {
private static class TestResource extends AcmeResource {
private static final long serialVersionUID = 1023419539450677538L;
public TestResource(Session session, URL location) {
super(session);
setLocation(location);
public TestResource(Login login, URL location) {
super(login, location);
}
}

View File

@ -24,6 +24,7 @@ import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
@ -120,7 +121,7 @@ public class AbstractAcmeProviderTest {
*/
@Test
public void testCreateChallenge() {
Session session = mock(Session.class);
Login login = mock(Login.class);
AbstractAcmeProvider provider = new AbstractAcmeProvider() {
@Override
@ -134,14 +135,14 @@ public class AbstractAcmeProviderTest {
}
};
Challenge c1 = provider.createChallenge(session, getJSON("httpChallenge"));
Challenge c1 = provider.createChallenge(login, getJSON("httpChallenge"));
assertThat(c1, not(nullValue()));
assertThat(c1, instanceOf(Http01Challenge.class));
Challenge c2 = provider.createChallenge(session, getJSON("httpChallenge"));
Challenge c2 = provider.createChallenge(login, getJSON("httpChallenge"));
assertThat(c2, not(sameInstance(c1)));
Challenge c3 = provider.createChallenge(session, getJSON("dnsChallenge"));
Challenge c3 = provider.createChallenge(login, getJSON("dnsChallenge"));
assertThat(c3, not(nullValue()));
assertThat(c3, instanceOf(Dns01Challenge.class));
@ -149,7 +150,7 @@ public class AbstractAcmeProviderTest {
.put("type", "foobar-01")
.put("url", "https://example.com/some/challenge")
.toJSON();
Challenge c6 = provider.createChallenge(session, json6);
Challenge c6 = provider.createChallenge(login, json6);
assertThat(c6, not(nullValue()));
assertThat(c6, instanceOf(Challenge.class));
@ -158,7 +159,7 @@ public class AbstractAcmeProviderTest {
.put("token", "abc123")
.put("url", "https://example.com/some/challenge")
.toJSON();
Challenge c7 = provider.createChallenge(session, json7);
Challenge c7 = provider.createChallenge(login, json7);
assertThat(c7, not(nullValue()));
assertThat(c7, instanceOf(TokenChallenge.class));
@ -166,14 +167,14 @@ public class AbstractAcmeProviderTest {
JSON json8 = new JSONBuilder()
.put("url", "https://example.com/some/challenge")
.toJSON();
provider.createChallenge(session, json8);
provider.createChallenge(login, json8);
fail("Challenge without type was accepted");
} catch (AcmeProtocolException ex) {
// expected
}
try {
provider.createChallenge(session, null);
provider.createChallenge(login, null);
fail("null was accepted");
} catch (NullPointerException ex) {
// expected

View File

@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.connector.Connection;
@ -35,7 +36,7 @@ import org.shredzone.acme4j.toolbox.TestUtils;
* of {@link Connection} that is always returned on {@link #connect()}.
*/
public class TestableConnectionProvider extends DummyConnection implements AcmeProvider {
private final Map<String, BiFunction<Session, JSON, Challenge>> creatorMap = new HashMap<>();
private final Map<String, BiFunction<Login, JSON, Challenge>> creatorMap = new HashMap<>();
private final Map<String, Challenge> createdMap = new HashMap<>();
private final JSONBuilder directory = new JSONBuilder();
@ -59,7 +60,7 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
* @param creator
* Creator {@link BiFunction} that creates a matching {@link Challenge}
*/
public void putTestChallenge(String type, BiFunction<Session, JSON, Challenge> creator) {
public void putTestChallenge(String type, BiFunction<Login, JSON, Challenge> creator) {
creatorMap.put(type, creator);
}
@ -80,10 +81,18 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
/**
* Creates a {@link Session} that uses this {@link AcmeProvider}.
*/
public Session createSession() throws IOException {
public Session createSession() {
return TestUtils.session(this);
}
/**
* Creates a {@link Login} that uses this {@link AcmeProvider}.
*/
public Login createLogin() throws IOException {
Session session = createSession();
return session.login(new URL(TestUtils.ACCOUNT_URL), TestUtils.createKeyPair());
}
@Override
public boolean accepts(URI serverUri) {
throw new UnsupportedOperationException();
@ -108,7 +117,7 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
}
@Override
public Challenge createChallenge(Session session, JSON data) {
public Challenge createChallenge(Login login, JSON data) {
if (creatorMap.isEmpty()) {
throw new UnsupportedOperationException();
}
@ -117,9 +126,9 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
String type = data.get("type").asString();
if (creatorMap.containsKey(type)) {
created = creatorMap.get(type).apply(session, data);
created = creatorMap.get(type).apply(login, data);
} else {
created = new Challenge(session, data);
created = new Challenge(login, data);
}
createdMap.put(type, created);

View File

@ -51,6 +51,7 @@ import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.keys.HmacKey;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.provider.AcmeProvider;
@ -70,9 +71,11 @@ public final class TestUtils {
public static final String D_THUMBPRINT = "0VPbh7-I6swlkBu0TrNKSQp6d69bukzeQA0ksuX3FFs";
public static final String ACME_SERVER_URI = "https://example.com/acme";
public static final String ACCOUNT_URL = "https://example.com/acme/account/1";
public static final byte[] DUMMY_NONCE = "foo-nonce-foo".getBytes();
private TestUtils() {
// utility class without constructor
}
@ -114,9 +117,20 @@ public final class TestUtils {
/**
* Creates a {@link Session} instance. It uses {@link #ACME_SERVER_URI} as server URI.
*/
public static Session session() throws IOException {
KeyPair keyPair = createKeyPair();
return new Session(URI.create(ACME_SERVER_URI), keyPair);
public static Session session() {
return new Session(URI.create(ACME_SERVER_URI));
}
/**
* Creates a {@link Login} instance. It uses {@link #ACME_SERVER_URI} as server URI,
* {@link #ACCOUNT_URL} as account URL, and a random key pair.
*/
public static Login login() {
try {
return session().login(new URL(ACCOUNT_URL), createKeyPair());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
/**
@ -141,9 +155,8 @@ public final class TestUtils {
* @param provider
* {@link AcmeProvider} to be used in this session
*/
public static Session session(final AcmeProvider provider) throws IOException {
KeyPair keyPair = createKeyPair();
return new Session(URI.create(ACME_SERVER_URI), keyPair) {
public static Session session(final AcmeProvider provider) {
return new Session(URI.create(ACME_SERVER_URI)) {
@Override
public AcmeProvider provider() {
return provider;

View File

@ -77,11 +77,11 @@ public class ClientTest {
// Create a session for Let's Encrypt.
// Use "acme://letsencrypt.org" for production server
Session session = new Session("acme://letsencrypt.org/staging", userKeyPair);
Session session = new Session("acme://letsencrypt.org/staging");
// Get the Account.
// If there is no account yet, create a new one.
Account acct = findOrRegisterAccount(session);
Account acct = findOrRegisterAccount(session, userKeyPair);
// Load or create a key pair for the domains. This should not be the userKeyPair!
KeyPair domainKeyPair = loadOrCreateDomainKeyPair();
@ -179,19 +179,22 @@ public class ClientTest {
*
* @param session
* {@link Session} to bind with
* @return {@link Account} connected to your account
* @return {@link Login} that is connected to your account
*/
private Account findOrRegisterAccount(Session session) throws AcmeException {
private Account findOrRegisterAccount(Session session, KeyPair accountKey) throws AcmeException {
// Ask the user to accept the TOS, if server provides us with a link.
URI tos = session.getMetadata().getTermsOfService();
if (tos != null) {
acceptAgreement(tos);
}
Account acct = new AccountBuilder().agreeToTermsOfService().create(session);
LOG.info("Registered a new user, URL: " + acct.getLocation());
Account account = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(accountKey)
.create(session);
LOG.info("Registered a new user, URL: " + account.getLocation());
return acct;
return account;
}
/**

View File

@ -53,11 +53,12 @@ public class OrderHttpIT {
*/
@Test
public void testHttpValidation() throws Exception {
Session session = new Session(boulderURI());
KeyPair keyPair = createKeyPair();
Session session = new Session(boulderURI(), keyPair);
Account account = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
KeyPair domainKeyPair = createKeyPair();

View File

@ -24,6 +24,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.AccountBuilder;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeException;
@ -41,25 +42,29 @@ public class AccountIT extends PebbleITBase {
@Test
public void testCreate() throws AcmeException {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
// Register a new user
AccountBuilder ab = new AccountBuilder();
ab.addContact("mailto:acme@example.com");
ab.agreeToTermsOfService();
Login login = new AccountBuilder()
.addContact("mailto:acme@example.com")
.agreeToTermsOfService()
.useKeyPair(keyPair)
.createLogin(session);
Account acct = ab.create(session);
URL location = acct.getLocation();
URL location = login.getAccountLocation();
assertIsPebbleUrl(location);
assertThat(session.getAccountLocation(), is(location));
// Check registered data
Account acct = login.getAccount();
assertThat(acct.getLocation(), is(location));
assertThat(acct.getContacts(), contains(URI.create("mailto:acme@example.com")));
assertThat(acct.getStatus(), is(Status.VALID));
// Bind another Account object
Session session2 = new Session(pebbleURI(), keyPair);
Account acct2 = Account.bind(session2, location);
Session session2 = new Session(pebbleURI());
Login login2 = new Login(location, keyPair, session2);
assertThat(login2.getAccountLocation(), is(location));
Account acct2 = login2.getAccount();
assertThat(acct2.getLocation(), is(location));
assertThat(acct2.getContacts(), contains(URI.create("mailto:acme@example.com")));
assertThat(acct2.getStatus(), is(Status.VALID));
@ -73,21 +78,25 @@ public class AccountIT extends PebbleITBase {
KeyPair keyPair = createKeyPair();
// Register a new user
Session session1 = new Session(pebbleURI(), keyPair);
Account acct1 = new AccountBuilder()
Session session1 = new Session(pebbleURI());
Login login1 = new AccountBuilder()
.addContact("mailto:acme@example.com")
.agreeToTermsOfService()
.create(session1);
URL location1 = acct1.getLocation();
.useKeyPair(keyPair)
.createLogin(session1);
URL location1 = login1.getAccountLocation();
assertIsPebbleUrl(location1);
// Try to register the same account again
Session session2 = new Session(pebbleURI(), keyPair);
Account acct2 = new AccountBuilder()
Session session2 = new Session(pebbleURI());
Login login2 = new AccountBuilder()
.addContact("mailto:acme@example.com")
.agreeToTermsOfService()
.create(session2);
URL location2 = acct2.getLocation();
.useKeyPair(keyPair)
.createLogin(session2);
URL location2 = login2.getAccountLocation();
assertIsPebbleUrl(location2);
assertThat(location1, is(location2));
@ -100,21 +109,23 @@ public class AccountIT extends PebbleITBase {
public void testCreateOnlyExisting() throws AcmeException {
KeyPair keyPair = createKeyPair();
Session session1 = new Session(pebbleURI(), keyPair);
Account acct1 = new AccountBuilder()
Session session1 = new Session(pebbleURI());
Login login1 = new AccountBuilder()
.agreeToTermsOfService()
.create(session1);
URL location1 = acct1.getLocation();
assertIsPebbleUrl(location1);
assertThat(session1.getAccountLocation(), is(location1));
.useKeyPair(keyPair)
.createLogin(session1);
Session session2 = new Session(pebbleURI(), keyPair);
Account acct2 = new AccountBuilder()
URL location1 = login1.getAccountLocation();
assertIsPebbleUrl(location1);
Session session2 = new Session(pebbleURI());
Login login2 = new AccountBuilder()
.onlyExisting()
.create(session2);
URL location2 = acct2.getLocation();
.useKeyPair(keyPair)
.createLogin(session2);
URL location2 = login2.getAccountLocation();
assertIsPebbleUrl(location2);
assertThat(session2.getAccountLocation(), is(location2));
assertThat(location1, is(location2));
}
@ -127,8 +138,8 @@ public class AccountIT extends PebbleITBase {
public void testNotExisting() throws AcmeException {
try {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
new AccountBuilder().onlyExisting().create(session);
Session session = new Session(pebbleURI());
new AccountBuilder().onlyExisting().useKeyPair(keyPair).create(session);
fail("onlyExisting flag was ignored");
} catch (AcmeServerException ex) {
assertThat(ex.getType(), is(URI.create("urn:ietf:params:acme:error:accountDoesNotExist")));
@ -141,11 +152,12 @@ public class AccountIT extends PebbleITBase {
@Test
public void testModify() throws AcmeException {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Account acct = new AccountBuilder()
.addContact("mailto:acme@example.com")
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
URL location = acct.getLocation();
assertIsPebbleUrl(location);
@ -170,24 +182,27 @@ public class AccountIT extends PebbleITBase {
@Ignore // TODO PEBBLE: missing
public void testKeyChange() throws AcmeException {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Account acct = new AccountBuilder().agreeToTermsOfService().create(session);
Account acct = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
URL location = acct.getLocation();
KeyPair newKeyPair = createKeyPair();
acct.changeKey(newKeyPair);
try {
Session sessionOldKey = new Session(pebbleURI(), keyPair);
Account oldAccount = Account.bind(sessionOldKey, location);
Session sessionOldKey = new Session(pebbleURI());
Account oldAccount = sessionOldKey.login(location, keyPair).getAccount();
oldAccount.update();
} catch (AcmeUnauthorizedException ex) {
// Expected
}
Session sessionNewKey = new Session(pebbleURI(), newKeyPair);
Account newAccount = Account.bind(sessionNewKey, location);
Session sessionNewKey = new Session(pebbleURI());
Account newAccount = sessionNewKey.login(location, newKeyPair).getAccount();
assertThat(newAccount.getStatus(), is(Status.VALID));
}
@ -198,15 +213,18 @@ public class AccountIT extends PebbleITBase {
@Ignore // TODO PEBBLE: missing
public void testDeactivate() throws AcmeException {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Account acct = new AccountBuilder().agreeToTermsOfService().create(session);
Account acct = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
URL location = acct.getLocation();
acct.deactivate();
Session session2 = new Session(pebbleURI(), keyPair);
Account acct2 = Account.bind(session2, location);
Session session2 = new Session(pebbleURI());
Account acct2 = session2.login(location, keyPair).getAccount();
assertThat(acct2.getLocation(), is(location));
assertThat(acct2.getStatus(), is(Status.DEACTIVATED));
}

View File

@ -98,10 +98,11 @@ public class OrderIT extends PebbleITBase {
*/
private void orderCertificate(String domain, Validator validator) throws Exception {
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Account account = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
KeyPair domainKeyPair = createKeyPair();

View File

@ -55,10 +55,11 @@ public class OrderWildcardIT extends PebbleITBase {
public void testDnsValidation() throws Exception {
BammBammClient client = getBammBammClient();
KeyPair keyPair = createKeyPair();
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Account account = new AccountBuilder()
.agreeToTermsOfService()
.useKeyPair(keyPair)
.create(session);
KeyPair domainKeyPair = createKeyPair();

View File

@ -18,7 +18,6 @@ import static org.junit.Assert.assertThat;
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.net.URI;
import java.security.KeyPair;
import org.junit.Test;
import org.shredzone.acme4j.Metadata;
@ -31,11 +30,9 @@ import org.shredzone.acme4j.exception.AcmeException;
*/
public class SessionIT extends PebbleITBase {
private final KeyPair keyPair = createKeyPair();
@Test
public void testNonce() throws AcmeException {
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
// No nonce yet on a fresh session
assertThat(session.getNonce(), is(nullValue()));
@ -50,7 +47,7 @@ public class SessionIT extends PebbleITBase {
@Test
public void testResources() throws AcmeException {
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
assertIsPebbleUrl(session.resourceUrl(Resource.NEW_ACCOUNT));
assertIsPebbleUrl(session.resourceUrl(Resource.NEW_NONCE));
@ -59,7 +56,7 @@ public class SessionIT extends PebbleITBase {
@Test
public void testMetadata() throws AcmeException {
Session session = new Session(pebbleURI(), keyPair);
Session session = new Session(pebbleURI());
Metadata meta = session.getMetadata();
assertThat(meta, not(nullValue()));