001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2017 Richard "Shred" Körber 005 * http://acme4j.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.acme4j; 015 016import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; 017import static org.assertj.core.api.Assertions.assertThat; 018import static org.shredzone.acme4j.toolbox.TestUtils.getJSON; 019import static org.shredzone.acme4j.toolbox.TestUtils.url; 020 021import java.net.HttpURLConnection; 022import java.net.URI; 023import java.net.URL; 024import java.time.Duration; 025import java.util.concurrent.atomic.AtomicBoolean; 026 027import org.assertj.core.api.AutoCloseableSoftAssertions; 028import org.junit.jupiter.api.Test; 029import org.shredzone.acme4j.exception.AcmeNotSupportedException; 030import org.shredzone.acme4j.provider.TestableConnectionProvider; 031import org.shredzone.acme4j.toolbox.JSON; 032import org.shredzone.acme4j.toolbox.JSONBuilder; 033import org.shredzone.acme4j.toolbox.TestUtils; 034 035/** 036 * Unit tests for {@link Order}. 037 */ 038public class OrderTest { 039 040 private final URL locationUrl = url("http://example.com/acme/order/1234"); 041 private final URL finalizeUrl = url("https://example.com/acme/acct/1/order/1/finalize"); 042 043 /** 044 * Test that order is properly updated. 045 */ 046 @Test 047 public void testUpdate() throws Exception { 048 var provider = new TestableConnectionProvider() { 049 @Override 050 public int sendSignedPostAsGetRequest(URL url, Login login) { 051 assertThat(url).isEqualTo(locationUrl); 052 return HttpURLConnection.HTTP_OK; 053 } 054 055 @Override 056 public JSON readJsonResponse() { 057 return getJSON("updateOrderResponse"); 058 } 059 }; 060 061 var login = provider.createLogin(); 062 063 var order = new Order(login, locationUrl); 064 order.update(); 065 066 try (var softly = new AutoCloseableSoftAssertions()) { 067 softly.assertThat(order.getStatus()).isEqualTo(Status.PENDING); 068 softly.assertThat(order.getExpires().orElseThrow()).isEqualTo("2015-03-01T14:09:00Z"); 069 softly.assertThat(order.getLocation()).isEqualTo(locationUrl); 070 071 softly.assertThat(order.getIdentifiers()).containsExactlyInAnyOrder( 072 Identifier.dns("example.com"), 073 Identifier.dns("www.example.com")); 074 softly.assertThat(order.getNotBefore().orElseThrow()) 075 .isEqualTo("2016-01-01T00:00:00Z"); 076 softly.assertThat(order.getNotAfter().orElseThrow()) 077 .isEqualTo("2016-01-08T00:00:00Z"); 078 softly.assertThat(order.getCertificate().getLocation()) 079 .isEqualTo(url("https://example.com/acme/cert/1234")); 080 softly.assertThat(order.getFinalizeLocation()).isEqualTo(finalizeUrl); 081 082 softly.assertThat(order.isAutoRenewing()).isFalse(); 083 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 084 .isThrownBy(order::getAutoRenewalStartDate); 085 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 086 .isThrownBy(order::getAutoRenewalEndDate); 087 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 088 .isThrownBy(order::getAutoRenewalLifetime); 089 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 090 .isThrownBy(order::getAutoRenewalLifetimeAdjust); 091 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 092 .isThrownBy(order::isAutoRenewalGetEnabled); 093 softly.assertThatExceptionOfType(AcmeNotSupportedException.class) 094 .isThrownBy(order::getProfile); 095 096 softly.assertThat(order.getError()).isNotEmpty(); 097 softly.assertThat(order.getError().orElseThrow().getType()) 098 .isEqualTo(URI.create("urn:ietf:params:acme:error:connection")); 099 softly.assertThat(order.getError().flatMap(Problem::getDetail).orElseThrow()) 100 .isEqualTo("connection refused"); 101 102 var auths = order.getAuthorizations(); 103 softly.assertThat(auths).hasSize(2); 104 softly.assertThat(auths.stream()) 105 .map(Authorization::getLocation) 106 .containsExactlyInAnyOrder( 107 url("https://example.com/acme/authz/1234"), 108 url("https://example.com/acme/authz/2345")); 109 } 110 111 provider.close(); 112 } 113 114 /** 115 * Test lazy loading. 116 */ 117 @Test 118 public void testLazyLoading() throws Exception { 119 var requestWasSent = new AtomicBoolean(false); 120 121 var provider = new TestableConnectionProvider() { 122 @Override 123 public int sendSignedPostAsGetRequest(URL url, Login login) { 124 requestWasSent.set(true); 125 assertThat(url).isEqualTo(locationUrl); 126 return HttpURLConnection.HTTP_OK; 127 } 128 129 @Override 130 public JSON readJsonResponse() { 131 return getJSON("updateOrderResponse"); 132 } 133 }; 134 135 var login = provider.createLogin(); 136 137 var order = new Order(login, locationUrl); 138 139 try (var softly = new AutoCloseableSoftAssertions()) { 140 // Lazy loading 141 softly.assertThat(requestWasSent).isFalse(); 142 softly.assertThat(order.getCertificate().getLocation()) 143 .isEqualTo(url("https://example.com/acme/cert/1234")); 144 softly.assertThat(requestWasSent).isTrue(); 145 146 // Subsequent queries do not trigger another load 147 requestWasSent.set(false); 148 softly.assertThat(order.getCertificate().getLocation()) 149 .isEqualTo(url("https://example.com/acme/cert/1234")); 150 softly.assertThat(order.getStatus()).isEqualTo(Status.PENDING); 151 softly.assertThat(order.getExpires().orElseThrow()).isEqualTo("2015-03-01T14:09:00Z"); 152 softly.assertThat(requestWasSent).isFalse(); 153 } 154 155 provider.close(); 156 } 157 158 /** 159 * Test that order is properly finalized. 160 */ 161 @Test 162 public void testFinalize() throws Exception { 163 var csr = TestUtils.getResourceAsByteArray("/csr.der"); 164 165 var provider = new TestableConnectionProvider() { 166 private boolean isFinalized = false; 167 168 @Override 169 public int sendSignedPostAsGetRequest(URL url, Login login) { 170 assertThat(url).isEqualTo(locationUrl); 171 return HttpURLConnection.HTTP_OK; 172 } 173 174 @Override 175 public int sendSignedRequest(URL url, JSONBuilder claims, Login login) { 176 assertThat(url).isEqualTo(finalizeUrl); 177 assertThatJson(claims.toString()).isEqualTo(getJSON("finalizeRequest").toString()); 178 assertThat(login).isNotNull(); 179 isFinalized = true; 180 return HttpURLConnection.HTTP_OK; 181 } 182 183 @Override 184 public JSON readJsonResponse() { 185 return getJSON(isFinalized ? "finalizeResponse" : "updateOrderResponse"); 186 } 187 }; 188 189 var login = provider.createLogin(); 190 191 var order = new Order(login, locationUrl); 192 order.execute(csr); 193 194 try (var softly = new AutoCloseableSoftAssertions()) { 195 softly.assertThat(order.getStatus()).isEqualTo(Status.VALID); 196 softly.assertThat(order.getExpires().orElseThrow()).isEqualTo("2015-03-01T14:09:00Z"); 197 softly.assertThat(order.getLocation()).isEqualTo(locationUrl); 198 199 softly.assertThat(order.getIdentifiers()).containsExactlyInAnyOrder( 200 Identifier.dns("example.com"), 201 Identifier.dns("www.example.com")); 202 softly.assertThat(order.getNotBefore().orElseThrow()) 203 .isEqualTo("2016-01-01T00:00:00Z"); 204 softly.assertThat(order.getNotAfter().orElseThrow()) 205 .isEqualTo("2016-01-08T00:00:00Z"); 206 softly.assertThat(order.isAutoRenewalCertificate()).isFalse(); 207 softly.assertThat(order.getCertificate().getLocation()) 208 .isEqualTo(url("https://example.com/acme/cert/1234")); 209 softly.assertThatIllegalStateException() 210 .isThrownBy(order::getAutoRenewalCertificate); 211 softly.assertThat(order.getFinalizeLocation()).isEqualTo(finalizeUrl); 212 213 var auths = order.getAuthorizations(); 214 softly.assertThat(auths).hasSize(2); 215 softly.assertThat(auths.stream()) 216 .map(Authorization::getLocation) 217 .containsExactlyInAnyOrder( 218 url("https://example.com/acme/authz/1234"), 219 url("https://example.com/acme/authz/2345")); 220 } 221 222 provider.close(); 223 } 224 225 /** 226 * Test that order is properly updated. 227 */ 228 @Test 229 public void testAutoRenewUpdate() throws Exception { 230 var provider = new TestableConnectionProvider() { 231 @Override 232 public int sendSignedPostAsGetRequest(URL url, Login login) { 233 assertThat(url).isEqualTo(locationUrl); 234 return HttpURLConnection.HTTP_OK; 235 } 236 237 @Override 238 public JSON readJsonResponse() { 239 return getJSON("updateAutoRenewOrderResponse"); 240 } 241 }; 242 243 provider.putMetadata("auto-renewal", JSON.empty()); 244 245 var login = provider.createLogin(); 246 247 var order = new Order(login, locationUrl); 248 order.update(); 249 250 try (var softly = new AutoCloseableSoftAssertions()) { 251 softly.assertThat(order.isAutoRenewing()).isTrue(); 252 softly.assertThat(order.getAutoRenewalStartDate().orElseThrow()) 253 .isEqualTo("2016-01-01T00:00:00Z"); 254 softly.assertThat(order.getAutoRenewalEndDate()) 255 .isEqualTo("2017-01-01T00:00:00Z"); 256 softly.assertThat(order.getAutoRenewalLifetime()) 257 .isEqualTo(Duration.ofHours(168)); 258 softly.assertThat(order.getAutoRenewalLifetimeAdjust().orElseThrow()) 259 .isEqualTo(Duration.ofDays(6)); 260 softly.assertThat(order.getNotBefore()).isEmpty(); 261 softly.assertThat(order.getNotAfter()).isEmpty(); 262 softly.assertThat(order.isAutoRenewalGetEnabled()).isTrue(); 263 } 264 265 provider.close(); 266 } 267 268 /** 269 * Test that auto-renew order is properly finalized. 270 */ 271 @Test 272 public void testAutoRenewFinalize() throws Exception { 273 var provider = new TestableConnectionProvider() { 274 @Override 275 public int sendSignedPostAsGetRequest(URL url, Login login) { 276 assertThat(url).isEqualTo(locationUrl); 277 return HttpURLConnection.HTTP_OK; 278 } 279 280 @Override 281 public JSON readJsonResponse() { 282 return getJSON("finalizeAutoRenewResponse"); 283 } 284 }; 285 286 var login = provider.createLogin(); 287 var order = login.bindOrder(locationUrl); 288 289 try (var softly = new AutoCloseableSoftAssertions()) { 290 softly.assertThat(order.isAutoRenewalCertificate()).isTrue(); 291 softly.assertThat(order.getCertificate().getLocation()) 292 .isEqualTo(url("https://example.com/acme/cert/1234")); 293 softly.assertThat(order.getAutoRenewalCertificate().getLocation()) 294 .isEqualTo(url("https://example.com/acme/cert/1234")); 295 softly.assertThat(order.isAutoRenewing()).isTrue(); 296 softly.assertThat(order.getAutoRenewalStartDate().orElseThrow()) 297 .isEqualTo("2018-01-01T00:00:00Z"); 298 softly.assertThat(order.getAutoRenewalEndDate()) 299 .isEqualTo("2019-01-01T00:00:00Z"); 300 softly.assertThat(order.getAutoRenewalLifetime()) 301 .isEqualTo(Duration.ofHours(168)); 302 softly.assertThat(order.getAutoRenewalLifetimeAdjust().orElseThrow()) 303 .isEqualTo(Duration.ofDays(6)); 304 softly.assertThat(order.getNotBefore()).isEmpty(); 305 softly.assertThat(order.getNotAfter()).isEmpty(); 306 softly.assertThat(order.isAutoRenewalGetEnabled()).isTrue(); 307 } 308 309 provider.close(); 310 } 311 312 /** 313 * Test that auto-renew order is properly canceled. 314 */ 315 @Test 316 public void testCancel() throws Exception { 317 var provider = new TestableConnectionProvider() { 318 @Override 319 public int sendSignedRequest(URL url, JSONBuilder claims, Login login) { 320 var json = claims.toJSON(); 321 assertThat(json.get("status").asString()).isEqualTo("canceled"); 322 assertThat(url).isEqualTo(locationUrl); 323 assertThat(login).isNotNull(); 324 return HttpURLConnection.HTTP_OK; 325 } 326 327 @Override 328 public JSON readJsonResponse() { 329 return getJSON("canceledOrderResponse"); 330 } 331 }; 332 333 provider.putMetadata("auto-renewal", JSON.empty()); 334 335 var login = provider.createLogin(); 336 337 var order = new Order(login, locationUrl); 338 order.cancelAutoRenewal(); 339 340 assertThat(order.getStatus()).isEqualTo(Status.CANCELED); 341 342 provider.close(); 343 } 344 345}