Skip to content

Commit

Permalink
Merge pull request #393 from onelogin/exclude-joda-time
Browse files Browse the repository at this point in the history
Exclude joda time
  • Loading branch information
not-ol-github authored Nov 18, 2022
2 parents 10002ab + 1661960 commit 03afb44
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 127 deletions.
7 changes: 0 additions & 7 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@
<optional>true</optional>
</dependency>

<!-- date and time library for Java -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.6</version>
</dependency>

<!-- commons -->
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
40 changes: 20 additions & 20 deletions core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -829,7 +829,7 @@ public List<String> 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) {
Expand Down Expand Up @@ -889,7 +889,7 @@ public List<Instant> 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;
}
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
137 changes: 83 additions & 54 deletions core/src/main/java/com/onelogin/saml2/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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);
}

Expand All @@ -1843,36 +1844,40 @@ 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("-")) {
durationString = durationString.substring(1);
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();
}

/**
Expand All @@ -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;
}
Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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));
}

}
Loading

0 comments on commit 03afb44

Please sign in to comment.