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());
     }
 }

Reply via email to