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.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); | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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()); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user