rhtyd closed pull request #2893: ApiServer: signature v3 to accept more formats
URL: https://github.com/apache/cloudstack/pull/2893
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/server/src/main/java/com/cloud/api/ApiServer.java
b/server/src/main/java/com/cloud/api/ApiServer.java
index a97984a2be2..a30718b0650 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.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.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 @@
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 boolean verifyRequest(final Map<String, Object[]>
requestParameters, fina
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 9f046d11446..8c55268782e 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.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 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 190adeab2db..428666f0736 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.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 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 void zonedTimeFormatLegacy() throws ParseException {
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 void zonedTimeFormat() throws ParseException {
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());
}
}
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services