Skip to content

Commit 9531b5d

Browse files
Merge pull request #374 from maruTA-bis5/201-exclude-joda-time
Use java.time instead of joda-time
2 parents 6d7fc4d + 009ff69 commit 9531b5d

File tree

11 files changed

+160
-127
lines changed

11 files changed

+160
-127
lines changed

core/pom.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,6 @@
4444
<optional>true</optional>
4545
</dependency>
4646

47-
<!-- date and time library for Java -->
48-
<dependency>
49-
<groupId>joda-time</groupId>
50-
<artifactId>joda-time</artifactId>
51-
<version>2.10.6</version>
52-
</dependency>
53-
5447
<!-- commons -->
5548
<dependency>
5649
<groupId>org.apache.commons</groupId>

core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.io.IOException;
44
import java.security.PrivateKey;
55
import java.security.cert.X509Certificate;
6+
import java.time.Instant;
7+
import java.time.temporal.ChronoUnit;
68
import java.util.ArrayList;
79
import java.util.Calendar;
810
import java.util.HashMap;
@@ -16,8 +18,6 @@
1618
import com.onelogin.saml2.model.hsm.HSM;
1719

1820
import org.apache.commons.lang3.StringUtils;
19-
import org.joda.time.DateTime;
20-
import org.joda.time.Instant;
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323
import org.w3c.dom.Document;
@@ -297,10 +297,10 @@ public boolean isValid(String requestId) {
297297
}
298298

299299
// Check the session Expiration
300-
DateTime sessionExpiration = this.getSessionNotOnOrAfter();
300+
Instant sessionExpiration = this.getSessionNotOnOrAfter();
301301
if (sessionExpiration != null) {
302-
sessionExpiration = sessionExpiration.plus(Constants.ALOWED_CLOCK_DRIFT * 1000);
303-
if (sessionExpiration.isEqualNow() || sessionExpiration.isBeforeNow()) {
302+
sessionExpiration = ChronoUnit.SECONDS.addTo(sessionExpiration, Constants.ALOWED_CLOCK_DRIFT);
303+
if (Util.isEqualNow(sessionExpiration) || Util.isBeforeNow(sessionExpiration)) {
304304
throw new ValidationError("The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response", ValidationError.SESSION_EXPIRED);
305305
}
306306
}
@@ -401,18 +401,18 @@ private void validateSubjectConfirmation(String responseInResponseTo) throws XPa
401401
continue;
402402
}
403403

404-
DateTime noa = Util.parseDateTime(notOnOrAfter.getNodeValue());
405-
noa = noa.plus(Constants.ALOWED_CLOCK_DRIFT * 1000);
406-
if (noa.isEqualNow() || noa.isBeforeNow()) {
404+
Instant noa = Util.parseDateTime(notOnOrAfter.getNodeValue());
405+
noa = ChronoUnit.SECONDS.addTo(noa, Constants.ALOWED_CLOCK_DRIFT);
406+
if (Util.isEqualNow(noa) || Util.isBeforeNow(noa)) {
407407
validationIssues.add(new SubjectConfirmationIssue(i, "SubjectConfirmationData is no longer valid"));
408408
continue;
409409
}
410410

411411
Node notBefore = subjectConfirmationDataNodes.item(c).getAttributes().getNamedItem("NotBefore");
412412
if (notBefore != null) {
413-
DateTime nb = Util.parseDateTime(notBefore.getNodeValue());
414-
nb = nb.minus(Constants.ALOWED_CLOCK_DRIFT * 1000);
415-
if (nb.isAfterNow()) {
413+
Instant nb = Util.parseDateTime(notBefore.getNodeValue());
414+
nb = ChronoUnit.SECONDS.addTo(nb, Constants.ALOWED_CLOCK_DRIFT * -1);
415+
if (Util.isAfterNow(nb)) {
416416
validationIssues.add(new SubjectConfirmationIssue(i, "SubjectConfirmationData is not yet valid"));
417417
continue;
418418
}
@@ -829,7 +829,7 @@ public List<String> getIssuers() throws XPathExpressionException, ValidationErro
829829
*
830830
* @throws XPathExpressionException
831831
*/
832-
public DateTime getSessionNotOnOrAfter() throws XPathExpressionException {
832+
public Instant getSessionNotOnOrAfter() throws XPathExpressionException {
833833
String notOnOrAfter = null;
834834
NodeList entries = this.queryAssertion("/saml:AuthnStatement[@SessionNotOnOrAfter]");
835835
if (entries.getLength() > 0) {
@@ -889,7 +889,7 @@ public List<Instant> getAssertionNotOnOrAfter() throws XPathExpressionException
889889
for (int i = 0; i < notOnOrAfterNodes.getLength(); i++) {
890890
final Node notOnOrAfterAttribute = notOnOrAfterNodes.item(i).getAttributes().getNamedItem("NotOnOrAfter");
891891
if (notOnOrAfterAttribute != null) {
892-
notOnOrAfters.add(new Instant(notOnOrAfterAttribute.getNodeValue()));
892+
notOnOrAfters.add(Instant.parse(notOnOrAfterAttribute.getNodeValue()));
893893
}}
894894
return notOnOrAfters;
895895
}
@@ -1053,17 +1053,17 @@ public boolean validateTimestamps() throws ValidationError {
10531053
Node naAttribute = attrName.getNamedItem("NotOnOrAfter");
10541054
// validate NotOnOrAfter
10551055
if (naAttribute != null) {
1056-
DateTime notOnOrAfterDate = Util.parseDateTime(naAttribute.getNodeValue());
1057-
notOnOrAfterDate = notOnOrAfterDate.plus(Constants.ALOWED_CLOCK_DRIFT * 1000);
1058-
if (notOnOrAfterDate.isEqualNow() || notOnOrAfterDate.isBeforeNow()) {
1056+
Instant notOnOrAfterDate = Util.parseDateTime(naAttribute.getNodeValue());
1057+
notOnOrAfterDate = ChronoUnit.SECONDS.addTo(notOnOrAfterDate, Constants.ALOWED_CLOCK_DRIFT);
1058+
if (Util.isEqualNow(notOnOrAfterDate) || Util.isBeforeNow(notOnOrAfterDate)) {
10591059
throw new ValidationError("Could not validate timestamp: expired. Check system clock.", ValidationError.ASSERTION_EXPIRED);
10601060
}
10611061
}
10621062
// validate NotBefore
10631063
if (nbAttribute != null) {
1064-
DateTime notBeforeDate = Util.parseDateTime(nbAttribute.getNodeValue());
1065-
notBeforeDate = notBeforeDate.minus(Constants.ALOWED_CLOCK_DRIFT * 1000);
1066-
if (notBeforeDate.isAfterNow()) {
1064+
Instant notBeforeDate = Util.parseDateTime(nbAttribute.getNodeValue());
1065+
notBeforeDate = ChronoUnit.SECONDS.addTo(notBeforeDate, Constants.ALOWED_CLOCK_DRIFT * -1);
1066+
if (Util.isAfterNow(notBeforeDate)) {
10671067
throw new ValidationError("Could not validate timestamp: not yet valid. Check system clock.", ValidationError.ASSERTION_TOO_EARLY);
10681068
}
10691069
}
@@ -1341,7 +1341,7 @@ public Calendar getResponseIssueInstant() throws ValidationError {
13411341
return null;
13421342
final Calendar result = Calendar.getInstance();
13431343
try {
1344-
result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
1344+
result.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli());
13451345
} catch (final IllegalArgumentException e) {
13461346
throw new ValidationError(
13471347
"The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format",

core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.net.URL;
55
import java.security.PrivateKey;
66
import java.security.cert.X509Certificate;
7+
import java.time.Instant;
78
import java.util.ArrayList;
89
import java.util.Calendar;
910
import java.util.HashMap;
@@ -13,7 +14,6 @@
1314
import javax.xml.xpath.XPathExpressionException;
1415

1516
import org.apache.commons.lang3.text.StrSubstitutor;
16-
import org.joda.time.DateTime;
1717
import org.slf4j.Logger;
1818
import org.slf4j.LoggerFactory;
1919
import org.w3c.dom.Document;
@@ -446,8 +446,8 @@ public Boolean isValid() {
446446
// Check NotOnOrAfter
447447
if (rootElement.hasAttribute("NotOnOrAfter")) {
448448
String notOnOrAfter = rootElement.getAttribute("NotOnOrAfter");
449-
DateTime notOnOrAfterDate = Util.parseDateTime(notOnOrAfter);
450-
if (notOnOrAfterDate.isEqualNow() || notOnOrAfterDate.isBeforeNow()) {
449+
Instant notOnOrAfterDate = Util.parseDateTime(notOnOrAfter);
450+
if (Util.isEqualNow(notOnOrAfterDate) || Util.isBeforeNow(notOnOrAfterDate)) {
451451
throw new ValidationError("Could not validate timestamp: expired. Check system clock.", ValidationError.RESPONSE_EXPIRED);
452452
}
453453
}
@@ -571,7 +571,7 @@ public static Calendar getIssueInstant(Document samlLogoutRequestDocument) {
571571
if(issueInstantString == null)
572572
return null;
573573
issueInstant = Calendar.getInstance();
574-
issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
574+
issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli());
575575
} catch (Exception e) {}
576576
return issueInstant;
577577
}

core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ public Calendar getIssueInstant() throws ValidationError {
611611
return null;
612612
final Calendar result = Calendar.getInstance();
613613
try {
614-
result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
614+
result.setTimeInMillis(Util.parseDateTime(issueInstantString).toEpochMilli());
615615
} catch (final IllegalArgumentException e) {
616616
throw new ValidationError(
617617
"The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format",

core/src/main/java/com/onelogin/saml2/util/Util.java

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,22 @@
2727
import java.security.cert.CertificateFactory;
2828
import java.security.cert.X509Certificate;
2929
import java.security.spec.PKCS8EncodedKeySpec;
30+
import java.time.Clock;
31+
import java.time.Duration;
32+
import java.time.Instant;
33+
import java.time.Period;
34+
import java.time.ZoneOffset;
35+
import java.time.ZonedDateTime;
36+
import java.time.format.DateTimeFormatter;
37+
import java.time.format.DateTimeParseException;
38+
import java.time.temporal.TemporalAccessor;
39+
import java.time.temporal.TemporalAmount;
3040
import java.util.Arrays;
3141
import java.util.Calendar;
3242
import java.util.HashMap;
3343
import java.util.HashSet;
3444
import java.util.Iterator;
3545
import java.util.List;
36-
import java.util.Locale;
3746
import java.util.Map;
3847
import java.util.Set;
3948
import java.util.TimeZone;
@@ -75,13 +84,6 @@
7584
import org.apache.xml.security.signature.XMLSignature;
7685
import org.apache.xml.security.transforms.Transforms;
7786
import org.apache.xml.security.utils.XMLUtils;
78-
import org.joda.time.DateTime;
79-
import org.joda.time.DateTimeZone;
80-
import org.joda.time.Period;
81-
import org.joda.time.format.DateTimeFormatter;
82-
import org.joda.time.format.ISODateTimeFormat;
83-
import org.joda.time.format.ISOPeriodFormat;
84-
import org.joda.time.format.PeriodFormatter;
8587
import org.slf4j.Logger;
8688
import org.slf4j.LoggerFactory;
8789
import org.w3c.dom.Attr;
@@ -110,8 +112,7 @@ public final class Util {
110112
*/
111113
private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
112114

113-
private static final DateTimeFormatter DATE_TIME_FORMAT = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
114-
private static final DateTimeFormatter DATE_TIME_FORMAT_MILLS = ISODateTimeFormat.dateTime().withZoneUTC();
115+
private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC);
115116
public static final String UNIQUE_ID_PREFIX = "ONELOGIN_";
116117
public static final String RESPONSE_SIGNATURE_XPATH = "/samlp:Response/ds:Signature";
117118
public static final String ASSERTION_SIGNATURE_XPATH = "/samlp:Response/saml:Assertion/ds:Signature";
@@ -1826,10 +1827,10 @@ public static String generateUniqueID() {
18261827
*
18271828
* @return int The new timestamp, after the duration is applied.
18281829
*
1829-
* @throws IllegalArgumentException
1830+
* @throws DateTimeParseException
18301831
*/
1831-
public static long parseDuration(String duration) throws IllegalArgumentException {
1832-
TimeZone timeZone = DateTimeZone.UTC.toTimeZone();
1832+
public static long parseDuration(String duration) throws DateTimeParseException {
1833+
TimeZone timeZone = TimeZone.getTimeZone(ZoneOffset.UTC);
18331834
return parseDuration(duration, Calendar.getInstance(timeZone).getTimeInMillis() / 1000);
18341835
}
18351836

@@ -1843,36 +1844,40 @@ public static long parseDuration(String duration) throws IllegalArgumentExceptio
18431844
*
18441845
* @return the new timestamp, after the duration is applied In Seconds.
18451846
*
1846-
* @throws IllegalArgumentException
1847+
* @throws DateTimeParseException
18471848
*/
1848-
public static long parseDuration(String durationString, long timestamp) throws IllegalArgumentException {
1849+
public static long parseDuration(String durationString, long timestamp) throws DateTimeParseException {
18491850
boolean haveMinus = false;
18501851

18511852
if (durationString.startsWith("-")) {
18521853
durationString = durationString.substring(1);
18531854
haveMinus = true;
18541855
}
18551856

1856-
PeriodFormatter periodFormatter = ISOPeriodFormat.standard().withLocale(new Locale("UTC"));
1857-
Period period = periodFormatter.parsePeriod(durationString);
1857+
TemporalAmount amount;
1858+
if (durationString.startsWith("PT")) {
1859+
amount = Duration.parse(durationString);
1860+
} else {
1861+
amount = Period.parse(durationString);
1862+
}
18581863

1859-
DateTime dt = new DateTime(timestamp * 1000, DateTimeZone.UTC);
1864+
ZonedDateTime dt = Instant.ofEpochSecond(timestamp).atZone(ZoneOffset.UTC);
18601865

1861-
DateTime result = null;
1866+
ZonedDateTime result;
18621867
if (haveMinus) {
1863-
result = dt.minus(period);
1868+
result = dt.minus(amount);
18641869
} else {
1865-
result = dt.plus(period);
1870+
result = dt.plus(amount);
18661871
}
1867-
return result.getMillis() / 1000;
1872+
return result.toEpochSecond();
18681873
}
18691874

18701875
/**
18711876
* @return the unix timestamp that matches the current time.
18721877
*/
18731878
public static Long getCurrentTimeStamp() {
1874-
DateTime currentDate = new DateTime(DateTimeZone.UTC);
1875-
return currentDate.getMillis() / 1000;
1879+
ZonedDateTime currentDate = ZonedDateTime.now(clock);
1880+
return currentDate.toEpochSecond();
18761881
}
18771882

18781883
/**
@@ -1893,8 +1898,8 @@ public static long getExpireTime(String cacheDuration, String validUntil) {
18931898
}
18941899

18951900
if (validUntil != null && !StringUtils.isEmpty(validUntil)) {
1896-
DateTime dt = Util.parseDateTime(validUntil);
1897-
long validUntilTimeInt = dt.getMillis() / 1000;
1901+
Instant dt = Util.parseDateTime(validUntil);
1902+
long validUntilTimeInt = dt.toEpochMilli() / 1000;
18981903
if (expireTime == 0 || expireTime > validUntilTimeInt) {
18991904
expireTime = validUntilTimeInt;
19001905
}
@@ -1940,25 +1945,7 @@ public static long getExpireTime(String cacheDuration, long validUntil) {
19401945
* @return string with format yyyy-MM-ddTHH:mm:ssZ
19411946
*/
19421947
public static String formatDateTime(long timeInMillis) {
1943-
return DATE_TIME_FORMAT.print(timeInMillis);
1944-
}
1945-
1946-
/**
1947-
* Create string form time In Millis with format yyyy-MM-ddTHH:mm:ssZ
1948-
*
1949-
* @param time
1950-
* The time
1951-
* @param millis
1952-
* Defines if the time is in Millis
1953-
*
1954-
* @return string with format yyyy-MM-ddTHH:mm:ssZ
1955-
*/
1956-
public static String formatDateTime(long time, boolean millis) {
1957-
if (millis) {
1958-
return DATE_TIME_FORMAT_MILLS.print(time);
1959-
} else {
1960-
return formatDateTime(time);
1961-
}
1948+
return DATE_TIME_FORMAT.format(Instant.ofEpochMilli(timeInMillis));
19621949
}
19631950

19641951
/**
@@ -1969,15 +1956,9 @@ public static String formatDateTime(long time, boolean millis) {
19691956
*
19701957
* @return datetime
19711958
*/
1972-
public static DateTime parseDateTime(String dateTime) {
1973-
1974-
DateTime parsedData = null;
1975-
try {
1976-
parsedData = DATE_TIME_FORMAT.parseDateTime(dateTime);
1977-
} catch(Exception e) {
1978-
return DATE_TIME_FORMAT_MILLS.parseDateTime(dateTime);
1979-
}
1980-
return parsedData;
1959+
public static Instant parseDateTime(String dateTime) {
1960+
TemporalAccessor parsedData = DATE_TIME_FORMAT.parse(dateTime);
1961+
return Instant.from(parsedData);
19811962
}
19821963

19831964
/**
@@ -2007,5 +1988,53 @@ private static byte[] toBytesUtf8(String str) {
20071988
}
20081989
}
20091990

1991+
private static Clock clock = Clock.systemUTC();
1992+
1993+
/**
1994+
* Get current timestamp milliseconds.
1995+
*
1996+
* @return current timestamp
1997+
*/
1998+
public static long getCurrentTimeMillis() {
1999+
return clock.millis();
2000+
}
2001+
2002+
static void setFixedClock(Clock fixClock) {
2003+
clock = fixClock;
2004+
}
2005+
2006+
static void setSystemClock() {
2007+
clock = Clock.systemUTC();
2008+
}
2009+
2010+
/**
2011+
* Checks if specified instant is equal to now.
2012+
*
2013+
* @param instant the instant to compare to
2014+
* @return true if instant is equal to now
2015+
*/
2016+
public static boolean isEqualNow(Instant instant) {
2017+
return instant.equals(Instant.now(clock));
2018+
}
2019+
2020+
/**
2021+
* Checks if specified instant is before now.
2022+
*
2023+
* @param instant the instant to compare to
2024+
* @return true if instant is before now
2025+
*/
2026+
public static boolean isBeforeNow(Instant instant) {
2027+
return instant.isBefore(Instant.now(clock));
2028+
}
2029+
2030+
/**
2031+
* Checks if specified instant is after now.
2032+
*
2033+
* @param instant the instant to compare to
2034+
* @return true if instant is before now
2035+
*/
2036+
public static boolean isAfterNow(Instant instant) {
2037+
return instant.isAfter(Instant.now(clock));
2038+
}
20102039

20112040
}

0 commit comments

Comments
 (0)