diff --git a/src/main/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClient.java b/src/main/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClient.java index a6d219f..ee80438 100644 --- a/src/main/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClient.java +++ b/src/main/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClient.java @@ -32,8 +32,12 @@ import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Objects; @@ -91,11 +95,23 @@ public class ApacheCloudStackClient { private static final String APACHE_CLOUDSTACK_API_ENDPOINT = "/api"; /** - * This flag indicates if we are going to validade the server certificate in case of HTTPS connections. + * This flag indicates if we are going to validate the server certificate in case of HTTPS connections. * The default value is 'true', meaning that we always validate the server HTTPS certificate. */ protected boolean validateServerHttpsCertificate = true; + /** + * The validity time of the ACS request. + * The default value is {@value #requestValidity} . + */ + private int requestValidity = 30; + + /** + * This parameter controls if the expiration of requests is activated or not. + * It is activated by default. The validity of requests if defined by {@value #requestValidity} property. + */ + private boolean shouldRequestsExpire = true; + private Gson gson = new GsonBuilder().create(); private Logger logger = LoggerFactory.getLogger(getClass()); @@ -418,10 +434,47 @@ protected List createSortedCommandQueryList if (StringUtils.isNotBlank(this.apacheCloudStackUser.getApiKey())) { queryCommand.add(new ApacheCloudStackApiCommandParameter("apiKey", this.apacheCloudStackUser.getApiKey())); } + configureRequestExpiration(queryCommand); Collections.sort(queryCommand); return queryCommand; } + /** + * This method configures the request expiration if needed. + * It uses the value defined at {@link #requestValidity} to determine until when the request is valid. + * It also uses the parameter {@link #shouldRequestsExpire} to decide if it has to configure or not the validity of the request. + * Moreover, if the 'apacheCloudStackRequestList' contains the 'expires' it will only add a parameter called 'signatureVersion=3', in order to enable that override. + */ + protected void configureRequestExpiration(List apacheCloudStackRequestList) { + boolean isOverridingExpirationConfigs = apacheCloudStackRequestList.contains(new ApacheCloudStackApiCommandParameter("expires", StringUtils.EMPTY)); + if (!isOverridingExpirationConfigs && !shouldRequestsExpire) { + return; + } + apacheCloudStackRequestList.add(new ApacheCloudStackApiCommandParameter("signatureVersion", 3)); + if (isOverridingExpirationConfigs) { + return; + } + String expirationDataAsSring = createExpirationDate(); + apacheCloudStackRequestList.add(new ApacheCloudStackApiCommandParameter("expires", expirationDataAsSring)); + } + + /** + * This method creates the expiration date as a string according to the ISO 8601. + */ + protected String createExpirationDate() { + DateFormat acsIso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + return acsIso8601DateFormat.format(getExpirationDate()); + } + + /** + * Creates the expiration date, by adding the {@link #requestValidity} to the current time. + */ + protected Date getExpirationDate() { + Calendar now = Calendar.getInstance(); + now.add(Calendar.SECOND, requestValidity); + return now.getTime(); + } + /** * It executes the given request and converts the result into an object of the given type. * This method will change the response type to 'JSON'. To execute the request, it uses the method {@link #executeRequest(ApacheCloudStackRequest)}. @@ -436,4 +489,12 @@ public T executeRequest(ApacheCloudStackRequest request, Class clazz) { public void setValidateServerHttpsCertificate(boolean validateServerHttpsCertificate) { this.validateServerHttpsCertificate = validateServerHttpsCertificate; } + + public void setRequestValidity(int requestValidity) { + this.requestValidity = requestValidity; + } + + public void setShouldRequestsExpire(boolean shouldRequestsExpire) { + this.shouldRequestsExpire = shouldRequestsExpire; + } } diff --git a/src/test/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClientTest.java b/src/test/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClientTest.java index 423a144..bd21075 100644 --- a/src/test/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClientTest.java +++ b/src/test/java/br/com/autonomiccs/apacheCloudStack/client/ApacheCloudStackClientTest.java @@ -25,7 +25,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -285,6 +287,7 @@ public void getUrlEncodedValueTestValueWithSpaces() { } @Test + @SuppressWarnings("unchecked") public void createSortedCommandQueryListTestWithApiKey() { ApacheCloudStackRequest apacheCloudStackRequestMock = Mockito.mock(ApacheCloudStackRequest.class); Set params = new HashSet<>(); @@ -292,10 +295,12 @@ public void createSortedCommandQueryListTestWithApiKey() { params.add(new ApacheCloudStackApiCommandParameter("param1", "value1")); Mockito.doReturn(params).when(apacheCloudStackRequestMock).getParameters(); Mockito.when(apacheCloudStackClient.apacheCloudStackUser.getApiKey()).thenReturn("apiKey"); + Mockito.doNothing().when(apacheCloudStackClient).configureRequestExpiration(Mockito.anyList()); List sortedCommandQueryList = apacheCloudStackClient.createSortedCommandQueryList(apacheCloudStackRequestMock); Mockito.verify(apacheCloudStackRequestMock).getParameters(); + Mockito.verify(apacheCloudStackClient).configureRequestExpiration(Mockito.anyList()); Assert.assertEquals(3, sortedCommandQueryList.size()); Assert.assertEquals("apiKey", sortedCommandQueryList.get(0).getName()); @@ -304,6 +309,7 @@ public void createSortedCommandQueryListTestWithApiKey() { } @Test + @SuppressWarnings("unchecked") public void createSortedCommandQueryListTestWithourApiKey() { ApacheCloudStackRequest apacheCloudStackRequestMock = Mockito.mock(ApacheCloudStackRequest.class); Set params = new HashSet<>(); @@ -311,10 +317,12 @@ public void createSortedCommandQueryListTestWithourApiKey() { params.add(new ApacheCloudStackApiCommandParameter("param1", "value1")); Mockito.doReturn(params).when(apacheCloudStackRequestMock).getParameters(); Mockito.when(apacheCloudStackClient.apacheCloudStackUser.getApiKey()).thenReturn(null); + Mockito.doNothing().when(apacheCloudStackClient).configureRequestExpiration(Mockito.anyList()); List sortedCommandQueryList = apacheCloudStackClient.createSortedCommandQueryList(apacheCloudStackRequestMock); Mockito.verify(apacheCloudStackRequestMock).getParameters(); + Mockito.verify(apacheCloudStackClient).configureRequestExpiration(Mockito.anyList()); Assert.assertEquals(2, sortedCommandQueryList.size()); Assert.assertEquals("command", sortedCommandQueryList.get(0).getName()); @@ -552,4 +560,77 @@ public void executeUserLogoutTest() { inOrder.verify(apacheCloudStackClient).createApacheCloudStackApiUrlRequest(Mockito.any(ApacheCloudStackRequest.class), Mockito.eq(false)); inOrder.verify(apacheCloudStackClient).executeRequestGetResponseAsString(Mockito.eq(urlRequest), Mockito.any(CloseableHttpClient.class), Mockito.any(HttpContext.class)); } + + @Test + public void configureRequestExpirationTestRequestsShouldNotExpire() { + apacheCloudStackClient.setShouldRequestsExpire(false); + + ArrayList arrayList = new ArrayList<>(); + apacheCloudStackClient.configureRequestExpiration(arrayList); + + Assert.assertEquals(0, arrayList.size()); + } + + @Test + public void configureRequestExpirationTestRequestsShouldNotExpireUsingOverride() { + apacheCloudStackClient.setShouldRequestsExpire(false); + + ArrayList arrayList = new ArrayList<>(); + arrayList.add(new ApacheCloudStackApiCommandParameter("expires", "2011-10-10T12:00:00+0530")); + + apacheCloudStackClient.configureRequestExpiration(arrayList); + + Assert.assertEquals(2, arrayList.size()); + Mockito.verify(apacheCloudStackClient, Mockito.never()).createExpirationDate(); + } + + @Test + public void configureRequestExpirationTestRequestsShouldExpireUsingOverride() { + apacheCloudStackClient.setShouldRequestsExpire(true); + + ArrayList arrayList = new ArrayList<>(); + ApacheCloudStackApiCommandParameter expirationParameter = new ApacheCloudStackApiCommandParameter("expires", "2011-10-10T12:00:00+0530"); + arrayList.add(expirationParameter); + + apacheCloudStackClient.configureRequestExpiration(arrayList); + + Assert.assertEquals(2, arrayList.size()); + Assert.assertEquals(expirationParameter, arrayList.get(0)); + Mockito.verify(apacheCloudStackClient, Mockito.never()).createExpirationDate(); + } + + @Test + public void configureRequestExpirationTestRequestsShouldExpireWithoutOverride() { + apacheCloudStackClient.setShouldRequestsExpire(true); + + ArrayList arrayList = new ArrayList<>(); + + String expirationDate = "2011-10-10T12:00:00+0530"; + Mockito.doReturn(expirationDate).when(apacheCloudStackClient).createExpirationDate(); + + apacheCloudStackClient.configureRequestExpiration(arrayList); + + Assert.assertEquals(2, arrayList.size()); + Assert.assertEquals("signatureVersion", arrayList.get(0).getName()); + Assert.assertEquals(3, arrayList.get(0).getValue()); + Assert.assertEquals("expires", arrayList.get(1).getName()); + Assert.assertEquals(expirationDate, arrayList.get(1).getValue()); + + Mockito.verify(apacheCloudStackClient).createExpirationDate(); + } + + @Test + public void createExpirationDateTest() { + Calendar someMomentInTimeSpace = Calendar.getInstance(); + someMomentInTimeSpace.set(1999, 12, 31, 23, 59, 59); + someMomentInTimeSpace.set(Calendar.MILLISECOND, 0); + Mockito.doReturn(someMomentInTimeSpace.getTime()).when(apacheCloudStackClient).getExpirationDate(); + + String expirationDate = apacheCloudStackClient.createExpirationDate(); + + String expectedExpirationDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(someMomentInTimeSpace.getTime()); + Assert.assertEquals(expectedExpirationDate, expirationDate); + + Mockito.verify(apacheCloudStackClient).getExpirationDate(); + } }