This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new 17c164d api: signature v3 to accept more formats (#2893)
17c164d is described below
commit 17c164d59a218675f25f1faf583260032e535d80
Author: Yoan Blanc <[email protected]>
AuthorDate: Wed Oct 31 12:27:48 2018 +0100
api: signature v3 to accept more formats (#2893)
It does it by reusing the DateUtil helpers. DateUtil uses java.time.* as
that one knows how to deal
with timezones correctly.
The format expected by signatureVersion=3&expires=.... is quite limited.
It should accept the following formats that are containing a timezone
and/or milliseconds.
2018-10-01T08:12:14Z
2018-10-01T08:12:14+01:00
2018-10-01T08:12:14+0100
2018-10-01T08:12:14.000Z
2018-10-01T08:12:14.000+01:00
2018-10-01T08:12:14.000+0100
afaik only 2018-10-01T08:12:14+0100 is accepted by the current codebase.
This PR echoes other pull requests I made earlier this year. #2392 and #2867
Signed-off-by: Yoan Blanc <[email protected]>
---
server/src/main/java/com/cloud/api/ApiServer.java | 18 +++----
utils/src/main/java/com/cloud/utils/DateUtil.java | 30 ++++++++---
.../test/java/com/cloud/utils/DateUtilTest.java | 62 ++++++++++++++++++++--
3 files changed, 91 insertions(+), 19 deletions(-)
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java
b/server/src/main/java/com/cloud/api/ApiServer.java
index a97984a..a30718b 100644
--- a/server/src/main/java/com/cloud/api/ApiServer.java
+++ b/server/src/main/java/com/cloud/api/ApiServer.java
@@ -42,6 +42,7 @@ import com.cloud.user.User;
import com.cloud.user.UserAccount;
import com.cloud.user.UserVO;
import com.cloud.utils.ConstantTimeComparator;
+import com.cloud.utils.DateUtil;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
@@ -166,9 +167,7 @@ import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.Security;
-import java.text.DateFormat;
import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -228,7 +227,6 @@ public class ApiServer extends ManagerBase implements
HttpRequestHandler, ApiSer
private ApiAsyncJobDispatcher asyncDispatcher;
private static int s_workerCount = 0;
- private static final DateFormat DateFormatToUse = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private static Map<String, List<Class<?>>> s_apiNameCmdClassMap = new
HashMap<String, List<Class<?>>>();
private static ExecutorService s_executor = new ThreadPoolExecutor(10,
150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new
NamedThreadFactory(
@@ -883,14 +881,14 @@ public class ApiServer extends ManagerBase implements
HttpRequestHandler, ApiSer
s_logger.debug("Missing Expires parameter -- ignoring
request. Signature: " + signature + ", apiKey: " + apiKey);
return false;
}
- synchronized (DateFormatToUse) {
- try {
- expiresTS = DateFormatToUse.parse(expires);
- } catch (final ParseException pe) {
- s_logger.debug("Incorrect date format for Expires
parameter", pe);
- return false;
- }
+
+ try {
+ expiresTS = DateUtil.parseTZDateString(expires);
+ } catch (final ParseException pe) {
+ s_logger.debug("Incorrect date format for Expires
parameter", pe);
+ return false;
}
+
final Date now = new Date(System.currentTimeMillis());
if (expiresTS.before(now)) {
s_logger.debug("Request expired -- ignoring ...sig: " +
signature + ", apiKey: " + apiKey);
diff --git a/utils/src/main/java/com/cloud/utils/DateUtil.java
b/utils/src/main/java/com/cloud/utils/DateUtil.java
index 9f046d1..8c55268 100644
--- a/utils/src/main/java/com/cloud/utils/DateUtil.java
+++ b/utils/src/main/java/com/cloud/utils/DateUtil.java
@@ -26,6 +26,10 @@ import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.OffsetDateTime;
+
import com.cloud.utils.exception.CloudRuntimeException;
public class DateUtil {
@@ -33,19 +37,33 @@ public class DateUtil {
public static final String YYYYMMDD_FORMAT = "yyyyMMddHHmmss";
private static final DateFormat s_outputFormat = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
+ private static final DateTimeFormatter[] parseFormats = new
DateTimeFormatter[]{
+ DateTimeFormatter.ISO_OFFSET_DATE_TIME,
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"),
+ DateTimeFormatter.ISO_INSTANT,
+ // with milliseconds
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSX"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"),
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"),
+ // legacy and non-sensical format
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'Z")
+ };
+
public static Date currentGMTTime() {
// Date object always stores miliseconds offset based on GMT internally
return new Date();
}
- // yyyy-MM-ddTHH:mm:ssZZZZ or yyyy-MM-ddTHH:mm:ssZxxxx
public static Date parseTZDateString(String str) throws ParseException {
- try {
- return s_outputFormat.parse(str);
- } catch (ParseException e) {
- final DateFormat dfParse = new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'Z");
- return dfParse.parse(str);
+ for (DateTimeFormatter formatter : parseFormats) {
+ try {
+ OffsetDateTime dt = OffsetDateTime.parse(str, formatter);
+ return Date.from(dt.toInstant());
+ } catch (DateTimeParseException e) {
+ // do nothing
+ }
}
+ throw new ParseException("Unparseable date: \"" + str + "\"", 0);
}
public static Date parseDateString(TimeZone tz, String dateString) {
diff --git a/utils/src/test/java/com/cloud/utils/DateUtilTest.java
b/utils/src/test/java/com/cloud/utils/DateUtilTest.java
index 190adea..428666f 100644
--- a/utils/src/test/java/com/cloud/utils/DateUtilTest.java
+++ b/utils/src/test/java/com/cloud/utils/DateUtilTest.java
@@ -25,6 +25,10 @@ import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
+import java.time.format.DateTimeFormatter;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+
import com.cloud.utils.DateUtil.IntervalType;
import org.junit.Test;
@@ -32,7 +36,6 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DateUtilTest {
-
// command line test tool
public static void main(String[] args) {
TimeZone localTimezone = Calendar.getInstance().getTimeZone();
@@ -56,7 +59,7 @@ public class DateUtilTest {
String str = dfDate.format(time);
Date dtParsed = DateUtil.parseTZDateString(str);
- assertEquals(time.toString(), dtParsed.toString());
+ assertEquals(str, time.toString(), dtParsed.toString());
}
@Test
@@ -66,6 +69,59 @@ public class DateUtilTest {
String str = dfDate.format(time);
Date dtParsed = DateUtil.parseTZDateString(str);
- assertEquals(time.toString(), dtParsed.toString());
+ assertEquals(str, time.toString(), dtParsed.toString());
+ }
+
+ @Test
+ public void zonedTimeFormatIsoOffsetDateTime() throws ParseException {
+ Date time = new Date();
+ String str =
OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+
+ Date dtParsed = DateUtil.parseTZDateString(str);
+
+ assertEquals(str, time.toString(), dtParsed.toString());
+ }
+
+ @Test
+ public void zonedTimeFormatIsoInstant() throws ParseException {
+ Date time = new Date();
+ String str =
OffsetDateTime.now().format(DateTimeFormatter.ISO_INSTANT);
+
+ Date dtParsed = DateUtil.parseTZDateString(str);
+
+ assertEquals(str, time.toString(), dtParsed.toString());
+ }
+
+ @Test
+ public void zonedTimeFormatIsoOffsetDateTimeMs() throws ParseException {
+ Date time = new Date();
+ DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSX");
+ String str = OffsetDateTime.now().format(formatter);
+
+ Date dtParsed = DateUtil.parseTZDateString(str);
+
+ assertEquals(str, time.toString(), dtParsed.toString());
+ }
+
+ @Test
+ public void zonedTimeFormatIsoInstantMs() throws ParseException {
+ Date time = new Date();
+ DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'");
+ String str = OffsetDateTime.now(ZoneOffset.UTC).format(formatter);
+
+ Date dtParsed = DateUtil.parseTZDateString(str);
+
+ assertEquals(str, time.toString(), dtParsed.toString());
+ }
+
+ @Test
+ public void zonedTimeFormatIsoNoColonZMs() throws ParseException {
+ Date time = new Date();
+ DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ");
+ String str = OffsetDateTime.now().format(formatter);
+
+ Date dtParsed = DateUtil.parseTZDateString(str);
+
+ assertEquals(str, time.toString(), dtParsed.toString());
}
}