diff --git a/core/pom.xml b/core/pom.xml index bcf90b8b..9d7ccebd 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -44,13 +44,6 @@ true - - - joda-time - joda-time - 2.10.6 - - org.apache.commons diff --git a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java index 03932b2f..ec3b0abf 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -3,6 +3,8 @@ import java.io.IOException; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -16,8 +18,6 @@ import com.onelogin.saml2.model.hsm.HSM; import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.joda.time.Instant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -297,10 +297,10 @@ public boolean isValid(String requestId) { } // Check the session Expiration - DateTime sessionExpiration = this.getSessionNotOnOrAfter(); + Instant sessionExpiration = this.getSessionNotOnOrAfter(); if (sessionExpiration != null) { - sessionExpiration = sessionExpiration.plus(Constants.ALOWED_CLOCK_DRIFT * 1000); - if (sessionExpiration.isEqualNow() || sessionExpiration.isBeforeNow()) { + sessionExpiration = ChronoUnit.SECONDS.addTo(sessionExpiration, Constants.ALOWED_CLOCK_DRIFT); + if (Util.isEqualNow(sessionExpiration) || Util.isBeforeNow(sessionExpiration)) { throw new ValidationError("The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response", ValidationError.SESSION_EXPIRED); } } @@ -401,18 +401,18 @@ private void validateSubjectConfirmation(String responseInResponseTo) throws XPa continue; } - DateTime noa = Util.parseDateTime(notOnOrAfter.getNodeValue()); - noa = noa.plus(Constants.ALOWED_CLOCK_DRIFT * 1000); - if (noa.isEqualNow() || noa.isBeforeNow()) { + Instant noa = Util.parseDateTime(notOnOrAfter.getNodeValue()); + noa = ChronoUnit.SECONDS.addTo(noa, Constants.ALOWED_CLOCK_DRIFT); + if (Util.isEqualNow(noa) || Util.isBeforeNow(noa)) { validationIssues.add(new SubjectConfirmationIssue(i, "SubjectConfirmationData is no longer valid")); continue; } Node notBefore = subjectConfirmationDataNodes.item(c).getAttributes().getNamedItem("NotBefore"); if (notBefore != null) { - DateTime nb = Util.parseDateTime(notBefore.getNodeValue()); - nb = nb.minus(Constants.ALOWED_CLOCK_DRIFT * 1000); - if (nb.isAfterNow()) { + Instant nb = Util.parseDateTime(notBefore.getNodeValue()); + nb = ChronoUnit.SECONDS.addTo(nb, Constants.ALOWED_CLOCK_DRIFT * -1); + if (Util.isAfterNow(nb)) { validationIssues.add(new SubjectConfirmationIssue(i, "SubjectConfirmationData is not yet valid")); continue; } @@ -829,7 +829,7 @@ public List getIssuers() throws XPathExpressionException, ValidationErro * * @throws XPathExpressionException */ - public DateTime getSessionNotOnOrAfter() throws XPathExpressionException { + public Instant getSessionNotOnOrAfter() throws XPathExpressionException { String notOnOrAfter = null; NodeList entries = this.queryAssertion("/saml:AuthnStatement[@SessionNotOnOrAfter]"); if (entries.getLength() > 0) { @@ -889,7 +889,7 @@ public List getAssertionNotOnOrAfter() throws XPathExpressionException for (int i = 0; i < notOnOrAfterNodes.getLength(); i++) { final Node notOnOrAfterAttribute = notOnOrAfterNodes.item(i).getAttributes().getNamedItem("NotOnOrAfter"); if (notOnOrAfterAttribute != null) { - notOnOrAfters.add(new Instant(notOnOrAfterAttribute.getNodeValue())); + notOnOrAfters.add(Instant.parse(notOnOrAfterAttribute.getNodeValue())); }} return notOnOrAfters; } @@ -1053,17 +1053,17 @@ public boolean validateTimestamps() throws ValidationError { Node naAttribute = attrName.getNamedItem("NotOnOrAfter"); // validate NotOnOrAfter if (naAttribute != null) { - DateTime notOnOrAfterDate = Util.parseDateTime(naAttribute.getNodeValue()); - notOnOrAfterDate = notOnOrAfterDate.plus(Constants.ALOWED_CLOCK_DRIFT * 1000); - if (notOnOrAfterDate.isEqualNow() || notOnOrAfterDate.isBeforeNow()) { + Instant notOnOrAfterDate = Util.parseDateTime(naAttribute.getNodeValue()); + notOnOrAfterDate = ChronoUnit.SECONDS.addTo(notOnOrAfterDate, Constants.ALOWED_CLOCK_DRIFT); + if (Util.isEqualNow(notOnOrAfterDate) || Util.isBeforeNow(notOnOrAfterDate)) { throw new ValidationError("Could not validate timestamp: expired. Check system clock.", ValidationError.ASSERTION_EXPIRED); } } // validate NotBefore if (nbAttribute != null) { - DateTime notBeforeDate = Util.parseDateTime(nbAttribute.getNodeValue()); - notBeforeDate = notBeforeDate.minus(Constants.ALOWED_CLOCK_DRIFT * 1000); - if (notBeforeDate.isAfterNow()) { + Instant notBeforeDate = Util.parseDateTime(nbAttribute.getNodeValue()); + notBeforeDate = ChronoUnit.SECONDS.addTo(notBeforeDate, Constants.ALOWED_CLOCK_DRIFT * -1); + if (Util.isAfterNow(notBeforeDate)) { throw new ValidationError("Could not validate timestamp: not yet valid. Check system clock.", ValidationError.ASSERTION_TOO_EARLY); } } @@ -1341,7 +1341,7 @@ public Calendar getResponseIssueInstant() throws ValidationError { return null; final Calendar result = Calendar.getInstance(); try { - result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + result.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli()); } catch (final IllegalArgumentException e) { throw new ValidationError( "The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format", diff --git a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java index 954ae9e9..83d51767 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -4,6 +4,7 @@ import java.net.URL; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -13,7 +14,6 @@ import javax.xml.xpath.XPathExpressionException; import org.apache.commons.lang3.text.StrSubstitutor; -import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -446,8 +446,8 @@ public Boolean isValid() { // Check NotOnOrAfter if (rootElement.hasAttribute("NotOnOrAfter")) { String notOnOrAfter = rootElement.getAttribute("NotOnOrAfter"); - DateTime notOnOrAfterDate = Util.parseDateTime(notOnOrAfter); - if (notOnOrAfterDate.isEqualNow() || notOnOrAfterDate.isBeforeNow()) { + Instant notOnOrAfterDate = Util.parseDateTime(notOnOrAfter); + if (Util.isEqualNow(notOnOrAfterDate) || Util.isBeforeNow(notOnOrAfterDate)) { throw new ValidationError("Could not validate timestamp: expired. Check system clock.", ValidationError.RESPONSE_EXPIRED); } } @@ -571,7 +571,7 @@ public static Calendar getIssueInstant(Document samlLogoutRequestDocument) { if(issueInstantString == null) return null; issueInstant = Calendar.getInstance(); - issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli()); } catch (Exception e) {} return issueInstant; } diff --git a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java index 8fc1bc65..c8427caf 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -611,7 +611,7 @@ public Calendar getIssueInstant() throws ValidationError { return null; final Calendar result = Calendar.getInstance(); try { - result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + result.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli()); } catch (final IllegalArgumentException e) { throw new ValidationError( "The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format", diff --git a/core/src/main/java/com/onelogin/saml2/util/Util.java b/core/src/main/java/com/onelogin/saml2/util/Util.java index 8aa2cbe4..28034c34 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -27,13 +27,22 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.Period; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; @@ -75,13 +84,6 @@ import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.transforms.Transforms; import org.apache.xml.security.utils.XMLUtils; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.Period; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; -import org.joda.time.format.ISOPeriodFormat; -import org.joda.time.format.PeriodFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Attr; @@ -110,8 +112,7 @@ public final class Util { */ private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); - private static final DateTimeFormatter DATE_TIME_FORMAT = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC(); - private static final DateTimeFormatter DATE_TIME_FORMAT_MILLS = ISODateTimeFormat.dateTime().withZoneUTC(); + private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC); public static final String UNIQUE_ID_PREFIX = "ONELOGIN_"; public static final String RESPONSE_SIGNATURE_XPATH = "/samlp:Response/ds:Signature"; public static final String ASSERTION_SIGNATURE_XPATH = "/samlp:Response/saml:Assertion/ds:Signature"; @@ -1826,10 +1827,10 @@ public static String generateUniqueID() { * * @return int The new timestamp, after the duration is applied. * - * @throws IllegalArgumentException + * @throws DateTimeParseException */ - public static long parseDuration(String duration) throws IllegalArgumentException { - TimeZone timeZone = DateTimeZone.UTC.toTimeZone(); + public static long parseDuration(String duration) throws DateTimeParseException { + TimeZone timeZone = TimeZone.getTimeZone(ZoneOffset.UTC); return parseDuration(duration, Calendar.getInstance(timeZone).getTimeInMillis() / 1000); } @@ -1843,9 +1844,9 @@ public static long parseDuration(String duration) throws IllegalArgumentExceptio * * @return the new timestamp, after the duration is applied In Seconds. * - * @throws IllegalArgumentException + * @throws DateTimeParseException */ - public static long parseDuration(String durationString, long timestamp) throws IllegalArgumentException { + public static long parseDuration(String durationString, long timestamp) throws DateTimeParseException { boolean haveMinus = false; if (durationString.startsWith("-")) { @@ -1853,26 +1854,30 @@ public static long parseDuration(String durationString, long timestamp) throws I haveMinus = true; } - PeriodFormatter periodFormatter = ISOPeriodFormat.standard().withLocale(new Locale("UTC")); - Period period = periodFormatter.parsePeriod(durationString); + TemporalAmount amount; + if (durationString.startsWith("PT")) { + amount = Duration.parse(durationString); + } else { + amount = Period.parse(durationString); + } - DateTime dt = new DateTime(timestamp * 1000, DateTimeZone.UTC); + ZonedDateTime dt = Instant.ofEpochSecond(timestamp).atZone(ZoneOffset.UTC); - DateTime result = null; + ZonedDateTime result; if (haveMinus) { - result = dt.minus(period); + result = dt.minus(amount); } else { - result = dt.plus(period); + result = dt.plus(amount); } - return result.getMillis() / 1000; + return result.toEpochSecond(); } /** * @return the unix timestamp that matches the current time. */ public static Long getCurrentTimeStamp() { - DateTime currentDate = new DateTime(DateTimeZone.UTC); - return currentDate.getMillis() / 1000; + ZonedDateTime currentDate = ZonedDateTime.now(clock); + return currentDate.toEpochSecond(); } /** @@ -1893,8 +1898,8 @@ public static long getExpireTime(String cacheDuration, String validUntil) { } if (validUntil != null && !StringUtils.isEmpty(validUntil)) { - DateTime dt = Util.parseDateTime(validUntil); - long validUntilTimeInt = dt.getMillis() / 1000; + Instant dt = Util.parseDateTime(validUntil); + long validUntilTimeInt = dt.toEpochMilli() / 1000; if (expireTime == 0 || expireTime > validUntilTimeInt) { expireTime = validUntilTimeInt; } @@ -1940,25 +1945,7 @@ public static long getExpireTime(String cacheDuration, long validUntil) { * @return string with format yyyy-MM-ddTHH:mm:ssZ */ public static String formatDateTime(long timeInMillis) { - return DATE_TIME_FORMAT.print(timeInMillis); - } - - /** - * Create string form time In Millis with format yyyy-MM-ddTHH:mm:ssZ - * - * @param time - * The time - * @param millis - * Defines if the time is in Millis - * - * @return string with format yyyy-MM-ddTHH:mm:ssZ - */ - public static String formatDateTime(long time, boolean millis) { - if (millis) { - return DATE_TIME_FORMAT_MILLS.print(time); - } else { - return formatDateTime(time); - } + return DATE_TIME_FORMAT.format(Instant.ofEpochMilli(timeInMillis)); } /** @@ -1969,15 +1956,9 @@ public static String formatDateTime(long time, boolean millis) { * * @return datetime */ - public static DateTime parseDateTime(String dateTime) { - - DateTime parsedData = null; - try { - parsedData = DATE_TIME_FORMAT.parseDateTime(dateTime); - } catch(Exception e) { - return DATE_TIME_FORMAT_MILLS.parseDateTime(dateTime); - } - return parsedData; + public static Instant parseDateTime(String dateTime) { + TemporalAccessor parsedData = DATE_TIME_FORMAT.parse(dateTime); + return Instant.from(parsedData); } /** @@ -2007,5 +1988,53 @@ private static byte[] toBytesUtf8(String str) { } } + private static Clock clock = Clock.systemUTC(); + + /** + * Get current timestamp milliseconds. + * + * @return current timestamp + */ + public static long getCurrentTimeMillis() { + return clock.millis(); + } + + static void setFixedClock(Clock fixClock) { + clock = fixClock; + } + + static void setSystemClock() { + clock = Clock.systemUTC(); + } + + /** + * Checks if specified instant is equal to now. + * + * @param instant the instant to compare to + * @return true if instant is equal to now + */ + public static boolean isEqualNow(Instant instant) { + return instant.equals(Instant.now(clock)); + } + + /** + * Checks if specified instant is before now. + * + * @param instant the instant to compare to + * @return true if instant is before now + */ + public static boolean isBeforeNow(Instant instant) { + return instant.isBefore(Instant.now(clock)); + } + + /** + * Checks if specified instant is after now. + * + * @param instant the instant to compare to + * @return true if instant is before now + */ + public static boolean isAfterNow(Instant instant) { + return instant.isAfter(Instant.now(clock)); + } } diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java index 3d960ca7..7cec9efa 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java @@ -10,13 +10,10 @@ import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Constants; +import com.onelogin.saml2.util.DateTimeTestUtils; import com.onelogin.saml2.util.Util; import org.hamcrest.Matchers; -import org.joda.time.DateTime; -import org.joda.time.DateTimeUtils; -import org.joda.time.Instant; -import org.joda.time.format.ISODateTimeFormat; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -28,6 +25,7 @@ import org.xml.sax.SAXException; import java.io.IOException; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -61,13 +59,13 @@ public class AuthnResponseTest { @Before public void setDateTime() { - //All calls to Joda time check will use this timestamp as "now" value : - setDateTime("2020-06-01T00:00:00Z"); + //All calls to time check will use this timestamp as "now" value : + DateTimeTestUtils.setFixedDateTime("2020-06-01T00:00:00Z"); } @After public void goBackToNormal() { - DateTimeUtils.setCurrentMillisSystem(); + DateTimeTestUtils.setCurrentMillisSystem(); } /** @@ -1157,7 +1155,7 @@ public void testGetAssertionDetails() throws IOException, Error, XPathExpression final List notOnOrAfters = samlResponse.getAssertionNotOnOrAfter(); assertEquals("pfxa46574df-b3b0-a06a-23c8-636413198772", samlResponse.getAssertionId()); - assertThat(notOnOrAfters, contains(new Instant("2010-11-18T22:02:37Z"))); + assertThat(notOnOrAfters, contains(Instant.parse("2010-11-18T22:02:37Z"))); } @@ -1170,7 +1168,7 @@ public void testGetAssertionDetails_encrypted() throws IOException, Error, XPath final List notOnOrAfters = samlResponse.getAssertionNotOnOrAfter(); assertEquals("_519c2712648ee09a06d1f9a08e9e835715fea60267", samlResponse.getAssertionId()); - assertThat(notOnOrAfters, contains(new Instant("2055-06-07T20:17:08Z"))); + assertThat(notOnOrAfters, contains(Instant.parse("2055-06-07T20:17:08Z"))); } @@ -1187,7 +1185,7 @@ public void testGetAssertionDetails_multiple() throws Exception { final List notOnOrAfters = samlResponse.getAssertionNotOnOrAfter(); assertEquals("pfx7841991c-c73f-4035-e2ee-c170c0e1d3e4", samlResponse.getAssertionId()); - assertThat(notOnOrAfters, contains(new Instant("2120-06-17T14:53:44Z"), new Instant("2010-06-17T14:53:44Z"))); + assertThat(notOnOrAfters, contains(Instant.parse("2120-06-17T14:53:44Z"), Instant.parse("2010-06-17T14:53:44Z"))); } /** @@ -1449,7 +1447,7 @@ public void testGetSessionNotOnOrAfter() throws IOException, Error, XPathExpress Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); - assertEquals(1290203857000L, samlResponse.getSessionNotOnOrAfter().getMillis()); + assertEquals(1290203857000L, samlResponse.getSessionNotOnOrAfter().toEpochMilli()); samlResponseEncoded = Util.getFileAsString("data/responses/response2.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); @@ -1457,7 +1455,7 @@ public void testGetSessionNotOnOrAfter() throws IOException, Error, XPathExpress samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); - assertEquals(2696012228000L, samlResponse.getSessionNotOnOrAfter().getMillis()); + assertEquals(2696012228000L, samlResponse.getSessionNotOnOrAfter().toEpochMilli()); } /** @@ -2310,17 +2308,17 @@ public void testParseAzureB2CTimestamp() throws IOException, Error, XPathExpress assertEquals("No Signature found. SAML Response rejected", samlResponse.getError()); settings.setStrict(true); - setDateTime("2020-07-16T07:57:00Z"); + DateTimeTestUtils.setFixedDateTime("2020-07-16T07:57:00Z"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertFalse(samlResponse.isValid()); assertEquals("A valid SubjectConfirmation was not found on this Response: SubjectConfirmationData doesn't match a valid Recipient", samlResponse.getError()); - setDateTime("2020-07-01T00:00:00Z"); + DateTimeTestUtils.setFixedDateTime("2020-07-01T00:00:00Z"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertFalse(samlResponse.isValid()); assertEquals("Could not validate timestamp: not yet valid. Check system clock.", samlResponse.getError()); - setDateTime("2020-08-01T00:00:00Z"); + DateTimeTestUtils.setFixedDateTime("2020-08-01T00:00:00Z"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertFalse(samlResponse.isValid()); assertEquals("Could not validate timestamp: expired. Check system clock.", samlResponse.getError()); @@ -3333,9 +3331,5 @@ private static HttpRequest newHttpRequest(String requestURL, String samlResponse return new HttpRequest(requestURL, (String)null).addParameter("SAMLResponse", samlResponseEncoded); } - private void setDateTime(String ISOTimeStamp) { - DateTime dateTime = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC().parseDateTime(ISOTimeStamp); - DateTimeUtils.setCurrentMillisFixed(dateTime.toDate().getTime()); - } } diff --git a/core/src/test/java/com/onelogin/saml2/test/util/UtilsTest.java b/core/src/test/java/com/onelogin/saml2/test/util/UtilsTest.java index 2b047bc1..13012543 100644 --- a/core/src/test/java/com/onelogin/saml2/test/util/UtilsTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/util/UtilsTest.java @@ -31,6 +31,8 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; +import java.time.Instant; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -41,7 +43,6 @@ import org.apache.commons.codec.binary.Base64; import org.apache.xml.security.exceptions.XMLSecurityException; import org.apache.xml.security.signature.XMLSignatureException; -import org.joda.time.DateTime; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -1947,7 +1948,7 @@ public void testGenerateUniqueID_usesDefaultOnEmpty() { * * @see com.onelogin.saml2.util.Util#parseDuration */ - @Test(expected=IllegalArgumentException.class) + @Test(expected=DateTimeParseException.class) public void testParseDurationException() throws Exception { long timestamp = 1393876825L;// 2014-03-03 21:00:25 long parsedDuration = Util.parseDuration("aaa", timestamp); @@ -1982,8 +1983,8 @@ public void testParseDuration() throws Exception { try { String invalidDuration = "PT1Y"; Util.parseDuration(invalidDuration); - } catch (IllegalArgumentException anIllegalArgumentException) { - assertThat(anIllegalArgumentException.getMessage(), is("Invalid format: \"PT1Y\" is malformed at \"1Y\"")); + } catch (DateTimeParseException anDateTimeParseException) { + assertThat(anDateTimeParseException.getMessage(), is("Text cannot be parsed to a Duration")); } } @@ -2057,13 +2058,13 @@ public void testFormatDateTime() { public void testParseDateTime() { long time = 1386650371L; String datetime = "2013-12-10T04:39:31Z"; - DateTime parsedTime = Util.parseDateTime(datetime); - assertEquals(time, parsedTime.getMillis() / 1000); + Instant parsedTime = Util.parseDateTime(datetime); + assertEquals(time, parsedTime.toEpochMilli() / 1000); // Now test if toolkit supports miliseconds String datetime2 = "2013-12-10T04:39:31.120Z"; - DateTime parsedTime2 = Util.parseDateTime(datetime2); - assertEquals(time, parsedTime2.getMillis() / 1000); + Instant parsedTime2 = Util.parseDateTime(datetime2); + assertEquals(time, parsedTime2.toEpochMilli() / 1000); } /** diff --git a/core/src/test/java/com/onelogin/saml2/util/DateTimeTestUtils.java b/core/src/test/java/com/onelogin/saml2/util/DateTimeTestUtils.java new file mode 100644 index 00000000..0321fb3e --- /dev/null +++ b/core/src/test/java/com/onelogin/saml2/util/DateTimeTestUtils.java @@ -0,0 +1,24 @@ +package com.onelogin.saml2.util; + +import java.time.Clock; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +public class DateTimeTestUtils { + + /** + * Use system clock as "now". + */ + public static void setCurrentMillisSystem() { + Util.setSystemClock(); + } + + /** + * Use provided dateTime as "now". + * + * @param dateTime the timestamp + */ + public static void setFixedDateTime(String dateTime) { + Util.setFixedClock(Clock.fixed(ZonedDateTime.parse(dateTime).toInstant(), ZoneOffset.UTC)); + } +} diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 37d2f787..3cb2ab03 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -65,13 +65,6 @@ provided - - - joda-time - joda-time - 2.10.6 - - org.apache.commons diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 6d035df9..9d8f29cb 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -5,6 +5,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; +import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -17,8 +18,6 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; -import org.joda.time.DateTime; -import org.joda.time.Instant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +99,7 @@ public class Auth { /** * SessionNotOnOrAfter. When the user is logged, this stored it from the AuthnStatement of the SAML Response */ - private DateTime sessionExpiration; + private Instant sessionExpiration; /** * The ID of the last message processed @@ -1453,7 +1452,7 @@ public final String getSessionIndex() { /** * @return the SessionNotOnOrAfter of the assertion */ - public final DateTime getSessionExpiration() { + public final Instant getSessionExpiration() { return sessionExpiration; } diff --git a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java index cd45125f..53c82ef5 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -30,6 +30,7 @@ import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -40,7 +41,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.joda.time.Instant; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -1244,7 +1244,7 @@ public void testGetAssertionDetails() throws Exception { auth.processResponse(); assertThat(auth.getLastAssertionId(), is("pfxb26bb203-4e9d-8e74-a46e-def275ff4c7b")); - assertThat(auth.getLastAssertionNotOnOrAfter(), contains(new Instant("2053-08-23T06:57:01Z"))); + assertThat(auth.getLastAssertionNotOnOrAfter(), contains(Instant.parse("2053-08-23T06:57:01Z"))); } /** @@ -1275,7 +1275,7 @@ public void testGetSessionExpiration() throws Exception { assertNull(auth2.getSessionExpiration()); auth2.processResponse(); assertTrue(auth2.isAuthenticated()); - assertEquals(2639545021000L, auth2.getSessionExpiration().getMillis()); + assertEquals(2639545021000L, auth2.getSessionExpiration().toEpochMilli()); } /**