mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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 <yoan.blanc@exoscale.ch>
This commit is contained in:
parent
7479e2877f
commit
17c164d59a
@ -42,6 +42,7 @@ import com.cloud.user.User;
|
|||||||
import com.cloud.user.UserAccount;
|
import com.cloud.user.UserAccount;
|
||||||
import com.cloud.user.UserVO;
|
import com.cloud.user.UserVO;
|
||||||
import com.cloud.utils.ConstantTimeComparator;
|
import com.cloud.utils.ConstantTimeComparator;
|
||||||
|
import com.cloud.utils.DateUtil;
|
||||||
import com.cloud.utils.HttpUtils;
|
import com.cloud.utils.HttpUtils;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
@ -166,9 +167,7 @@ import java.net.URISyntaxException;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -228,7 +227,6 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
private ApiAsyncJobDispatcher asyncDispatcher;
|
private ApiAsyncJobDispatcher asyncDispatcher;
|
||||||
|
|
||||||
private static int s_workerCount = 0;
|
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 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(
|
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);
|
s_logger.debug("Missing Expires parameter -- ignoring request. Signature: " + signature + ", apiKey: " + apiKey);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
synchronized (DateFormatToUse) {
|
|
||||||
try {
|
try {
|
||||||
expiresTS = DateFormatToUse.parse(expires);
|
expiresTS = DateUtil.parseTZDateString(expires);
|
||||||
} catch (final ParseException pe) {
|
} catch (final ParseException pe) {
|
||||||
s_logger.debug("Incorrect date format for Expires parameter", pe);
|
s_logger.debug("Incorrect date format for Expires parameter", pe);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Date now = new Date(System.currentTimeMillis());
|
final Date now = new Date(System.currentTimeMillis());
|
||||||
if (expiresTS.before(now)) {
|
if (expiresTS.before(now)) {
|
||||||
s_logger.debug("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey);
|
s_logger.debug("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey);
|
||||||
|
|||||||
@ -26,6 +26,10 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
public class DateUtil {
|
public class DateUtil {
|
||||||
@ -33,19 +37,33 @@ public class DateUtil {
|
|||||||
public static final String YYYYMMDD_FORMAT = "yyyyMMddHHmmss";
|
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 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() {
|
public static Date currentGMTTime() {
|
||||||
// Date object always stores miliseconds offset based on GMT internally
|
// Date object always stores miliseconds offset based on GMT internally
|
||||||
return new Date();
|
return new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
// yyyy-MM-ddTHH:mm:ssZZZZ or yyyy-MM-ddTHH:mm:ssZxxxx
|
|
||||||
public static Date parseTZDateString(String str) throws ParseException {
|
public static Date parseTZDateString(String str) throws ParseException {
|
||||||
try {
|
for (DateTimeFormatter formatter : parseFormats) {
|
||||||
return s_outputFormat.parse(str);
|
try {
|
||||||
} catch (ParseException e) {
|
OffsetDateTime dt = OffsetDateTime.parse(str, formatter);
|
||||||
final DateFormat dfParse = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'Z");
|
return Date.from(dt.toInstant());
|
||||||
return dfParse.parse(str);
|
} catch (DateTimeParseException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
throw new ParseException("Unparseable date: \"" + str + "\"", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Date parseDateString(TimeZone tz, String dateString) {
|
public static Date parseDateString(TimeZone tz, String dateString) {
|
||||||
|
|||||||
@ -25,6 +25,10 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
|
||||||
import com.cloud.utils.DateUtil.IntervalType;
|
import com.cloud.utils.DateUtil.IntervalType;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -32,7 +36,6 @@ import org.junit.Test;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class DateUtilTest {
|
public class DateUtilTest {
|
||||||
|
|
||||||
// command line test tool
|
// command line test tool
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
TimeZone localTimezone = Calendar.getInstance().getTimeZone();
|
TimeZone localTimezone = Calendar.getInstance().getTimeZone();
|
||||||
@ -56,7 +59,7 @@ public class DateUtilTest {
|
|||||||
String str = dfDate.format(time);
|
String str = dfDate.format(time);
|
||||||
Date dtParsed = DateUtil.parseTZDateString(str);
|
Date dtParsed = DateUtil.parseTZDateString(str);
|
||||||
|
|
||||||
assertEquals(time.toString(), dtParsed.toString());
|
assertEquals(str, time.toString(), dtParsed.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -66,6 +69,59 @@ public class DateUtilTest {
|
|||||||
String str = dfDate.format(time);
|
String str = dfDate.format(time);
|
||||||
Date dtParsed = DateUtil.parseTZDateString(str);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user