Skip to content

Commit 211e54b

Browse files
authored
Merge pull request #320 from mauromol/add-last-message-issue-instant
Add Auth.getLastMessageIssueInstant and Auth.getLastRequestIssueInstant
2 parents f96a229 + bd6bc48 commit 211e54b

File tree

11 files changed

+322
-16
lines changed

11 files changed

+322
-16
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,13 @@ public String getId()
261261
{
262262
return id;
263263
}
264+
265+
/**
266+
* Returns the issue instant of this message.
267+
*
268+
* @return a new {@link Calendar} instance carrying the issue instant of this message
269+
*/
270+
public Calendar getIssueInstant() {
271+
return issueInstant == null? null: (Calendar) issueInstant.clone();
272+
}
264273
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.security.PrivateKey;
55
import java.security.cert.X509Certificate;
66
import java.util.ArrayList;
7+
import java.util.Calendar;
78
import java.util.HashMap;
89
import java.util.List;
910
import java.util.Map;
@@ -1243,4 +1244,30 @@ protected void validateSpNameQualifier(final String spNameQualifier) throws Vali
12431244
throw new ValidationError("The SPNameQualifier value mismatch the SP entityID value.", ValidationError.SP_NAME_QUALIFIER_NAME_MISMATCH);
12441245
}
12451246
}
1247+
1248+
/**
1249+
* Returns the issue instant of this message.
1250+
*
1251+
* @return a new {@link Calendar} instance carrying the issue instant of this message
1252+
* @throws ValidationError
1253+
* if the found IssueInstant attribute is not in the expected
1254+
* UTC form of ISO-8601 format
1255+
*/
1256+
public Calendar getResponseIssueInstant() throws ValidationError {
1257+
final Element rootElement = getSAMLResponseDocument()
1258+
.getDocumentElement();
1259+
final String issueInstantString = rootElement.hasAttribute(
1260+
"IssueInstant")? rootElement.getAttribute("IssueInstant"): null;
1261+
if(issueInstantString == null)
1262+
return null;
1263+
final Calendar result = Calendar.getInstance();
1264+
try {
1265+
result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
1266+
} catch (final IllegalArgumentException e) {
1267+
throw new ValidationError(
1268+
"The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format",
1269+
ValidationError.INVALID_ISSUE_INSTANT_FORMAT);
1270+
}
1271+
return result;
1272+
}
12461273
}

core/src/main/java/com/onelogin/saml2/exception/ValidationError.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class ValidationError extends SAMLException {
5353
public static final int NOT_SUPPORTED = 46;
5454
public static final int KEY_ALGORITHM_ERROR = 47;
5555
public static final int MISSING_ENCRYPTED_ELEMENT = 48;
56+
public static final int INVALID_ISSUE_INSTANT_FORMAT = 49;
5657

5758
private int errorCode;
5859

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

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId,
142142
logoutRequestString = substitutor.replace(getLogoutRequestTemplate());
143143
} else {
144144
logoutRequestString = Util.base64decodedInflated(samlLogoutRequest);
145-
id = getId(logoutRequestString);
145+
Document doc = Util.loadXML(logoutRequestString);
146+
id = getId(doc);
147+
issueInstant = getIssueInstant(doc);
146148
}
147149
}
148150

@@ -473,14 +475,14 @@ public Boolean isValid() {
473475
}
474476
}
475477

476-
/**
477-
* Returns the ID of the Logout Request Document.
478-
*
478+
/**
479+
* Returns the ID of the Logout Request Document.
480+
*
479481
* @param samlLogoutRequestDocument
480482
* A DOMDocument object loaded from the SAML Logout Request.
481483
*
482-
* @return the ID of the Logout Request.
483-
*/
484+
* @return the ID of the Logout Request.
485+
*/
484486
public static String getId(Document samlLogoutRequestDocument) {
485487
String id = null;
486488
try {
@@ -491,19 +493,55 @@ public static String getId(Document samlLogoutRequestDocument) {
491493
return id;
492494
}
493495

494-
/**
495-
* Returns the ID of the Logout Request String.
496-
*
497-
* @param samlLogoutRequestString
498-
* A Logout Request string.
499-
*
500-
* @return the ID of the Logout Request.
501-
*
502-
*/
496+
/**
497+
* Returns the issue instant of the Logout Request Document.
498+
*
499+
* @param samlLogoutRequestDocument
500+
* A DOMDocument object loaded from the SAML Logout Request.
501+
*
502+
* @return the issue instant of the Logout Request.
503+
*/
504+
public static Calendar getIssueInstant(Document samlLogoutRequestDocument) {
505+
Calendar issueInstant = null;
506+
try {
507+
Element rootElement = samlLogoutRequestDocument.getDocumentElement();
508+
rootElement.normalize();
509+
String issueInstantString = rootElement.hasAttribute(
510+
"IssueInstant")? rootElement.getAttribute("IssueInstant"): null;
511+
if(issueInstantString == null)
512+
return null;
513+
issueInstant = Calendar.getInstance();
514+
issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
515+
} catch (Exception e) {}
516+
return issueInstant;
517+
}
518+
519+
/**
520+
* Returns the ID of the Logout Request String.
521+
*
522+
* @param samlLogoutRequestString
523+
* A Logout Request string.
524+
*
525+
* @return the ID of the Logout Request.
526+
*
527+
*/
503528
public static String getId(String samlLogoutRequestString) {
504529
Document doc = Util.loadXML(samlLogoutRequestString);
505530
return getId(doc);
506531
}
532+
533+
/**
534+
* Returns the issue instant of the Logout Request Document.
535+
*
536+
* @param samlLogoutRequestDocument
537+
* A DOMDocument object loaded from the SAML Logout Request.
538+
*
539+
* @return the issue instant of the Logout Request.
540+
*/
541+
public static Calendar getIssueInstant(String samlLogoutRequestString) {
542+
Document doc = Util.loadXML(samlLogoutRequestString);
543+
return getIssueInstant(doc);
544+
}
507545

508546
/**
509547
* Gets the NameID Data from the the Logout Request Document.
@@ -762,4 +800,13 @@ public String getId()
762800
{
763801
return id;
764802
}
803+
804+
/**
805+
* Returns the issue instant of this message.
806+
*
807+
* @return a new {@link Calendar} instance carrying the issue instant of this message
808+
*/
809+
public Calendar getIssueInstant() {
810+
return issueInstant == null? null: (Calendar) issueInstant.clone();
811+
}
765812
}

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public Exception getValidationException() {
478478
}
479479

480480
/**
481-
* Sets the validation exception that this {@link LogoutResponse} should return
481+
* Sets the validation exception that this {@link LogoutResponse} should return
482482
* when a validation error occurs.
483483
*
484484
* @param validationException
@@ -487,4 +487,33 @@ public Exception getValidationException() {
487487
protected void setValidationException(Exception validationException) {
488488
this.validationException = validationException;
489489
}
490+
491+
/**
492+
* Returns the issue instant of this message.
493+
*
494+
* @return a new {@link Calendar} instance carrying the issue instant of this message
495+
* @throws ValidationError
496+
* if this logout response was received and parsed and the found IssueInstant
497+
* attribute is not in the expected UTC form of ISO-8601 format
498+
*/
499+
public Calendar getIssueInstant() throws ValidationError {
500+
if(logoutResponseDocument != null) {
501+
final Element rootElement = logoutResponseDocument
502+
.getDocumentElement();
503+
final String issueInstantString = rootElement.hasAttribute(
504+
"IssueInstant")? rootElement.getAttribute("IssueInstant"): null;
505+
if(issueInstantString == null)
506+
return null;
507+
final Calendar result = Calendar.getInstance();
508+
try {
509+
result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis());
510+
} catch (final IllegalArgumentException e) {
511+
throw new ValidationError(
512+
"The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format",
513+
ValidationError.INVALID_ISSUE_INSTANT_FORMAT);
514+
}
515+
return result;
516+
} else
517+
return issueInstant == null? null: (Calendar) issueInstant.clone();
518+
}
490519
}

core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
import static org.hamcrest.CoreMatchers.not;
55
import static org.junit.Assert.assertEquals;
66
import static org.junit.Assert.assertNotEquals;
7+
import static org.junit.Assert.assertNotNull;
78
import static org.junit.Assert.assertThat;
9+
import static org.junit.Assert.assertTrue;
810

911
import java.util.ArrayList;
12+
import java.util.Calendar;
1013
import java.util.List;
1114

15+
import org.junit.Assert;
1216
import org.junit.Test;
1317

1418
import com.onelogin.saml2.authn.AuthnRequest;
@@ -327,6 +331,32 @@ public void testGetId() throws Exception
327331
assertThat(authnRequestStr, containsString("ID=\"" + authnRequest.getId() + "\""));
328332
}
329333

334+
/**
335+
* Tests the getId method of AuthnRequest
336+
*
337+
* @throws Exception
338+
*
339+
* @see com.onelogin.saml2.authn.AuthnRequest.getId
340+
*/
341+
@Test
342+
public void testGetIssueInstant() throws Exception
343+
{
344+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build();
345+
346+
final long start = System.currentTimeMillis();
347+
AuthnRequest authnRequest = new AuthnRequest(settings);
348+
final long end = System.currentTimeMillis();
349+
final String authnRequestStr = Util.base64decodedInflated(authnRequest.getEncodedAuthnRequest());
350+
351+
final Calendar issueInstant = authnRequest.getIssueInstant();
352+
assertNotNull(issueInstant);
353+
final long millis = issueInstant.getTimeInMillis();
354+
assertTrue(millis >= start && millis <= end);
355+
356+
assertThat(authnRequestStr, containsString("<samlp:AuthnRequest"));
357+
assertThat(authnRequestStr, containsString("IssueInstant=\"" + Util.formatDateTime(millis) + "\""));
358+
}
359+
330360
/**
331361
* Tests the AuthnRequest Constructor
332362
* The creation of a deflated SAML Request with and without Destination

core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import java.io.IOException;
3131
import java.util.ArrayList;
32+
import java.util.Calendar;
3233
import java.util.HashMap;
3334
import java.util.List;
3435
import java.util.Map;
@@ -478,6 +479,29 @@ public void testGetNameIdData() throws Exception {
478479
assertTrue(samlResponse.getNameIdData().isEmpty());
479480
}
480481

482+
/**
483+
* Tests the getIssueInstant method of SamlResponse
484+
*
485+
* @throws Exception
486+
*
487+
* @see com.onelogin.saml2.authn.SamlResponse#getResponseIssueInstant()
488+
*/
489+
@Test
490+
public void testGetIssueInstant() throws Exception {
491+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build();
492+
String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64");
493+
SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
494+
assertEquals("2010-11-18T21:57:37Z", Util.formatDateTime(samlResponse.getResponseIssueInstant().getTimeInMillis()));
495+
496+
samlResponseEncoded = Util.getFileAsString("data/responses/response_encrypted_nameid.xml.base64");
497+
samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
498+
assertEquals("2014-03-09T12:23:37Z", Util.formatDateTime(samlResponse.getResponseIssueInstant().getTimeInMillis()));
499+
500+
samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64");
501+
samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
502+
assertEquals("2014-03-29T12:01:57Z", Util.formatDateTime(samlResponse.getResponseIssueInstant().getTimeInMillis()));
503+
}
504+
481505
/**
482506
* Tests the decryptAssertion method of SamlResponse
483507
* Case: EncryptedAssertion with an encryptedData element with a KeyInfo

core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import static org.junit.Assert.assertNull;
88
import static org.junit.Assert.assertFalse;
99
import static org.junit.Assert.assertNotEquals;
10+
import static org.junit.Assert.assertNotNull;
1011
import static org.junit.Assert.assertTrue;
1112

1213
import java.util.ArrayList;
14+
import java.util.Calendar;
1315
import java.util.List;
1416
import java.io.IOException;
1517
import java.net.URISyntaxException;
@@ -24,6 +26,7 @@
2426
import org.junit.rules.ExpectedException;
2527

2628
import com.onelogin.saml2.logout.LogoutRequest;
29+
import com.onelogin.saml2.logout.LogoutResponse;
2730
import com.onelogin.saml2.http.HttpRequest;
2831
import com.onelogin.saml2.exception.XMLEntityException;
2932
import com.onelogin.saml2.exception.Error;
@@ -309,6 +312,56 @@ public void testGetId() throws Exception {
309312
assertNull(LogoutRequest.getId(""));
310313
}
311314

315+
/**
316+
* Tests the getIssueInstant method of LogoutRequest
317+
*
318+
* @throws Exception
319+
*
320+
* @see com.onelogin.saml2.logout.LogoutRequest#getIssueInstant(String)
321+
*/
322+
@Test
323+
public void testGetIssueInstant() throws Exception {
324+
String samlRequest = Util.getFileAsString("data/logout_requests/logout_request.xml");
325+
Calendar issueInstant = LogoutRequest.getIssueInstant(samlRequest);
326+
String expectedIssueInstant = "2013-12-10T04:39:31Z";
327+
assertEquals(expectedIssueInstant, Util.formatDateTime(issueInstant.getTimeInMillis()));
328+
329+
Document samlRequestDoc = Util.loadXML(samlRequest);
330+
issueInstant = LogoutRequest.getIssueInstant(samlRequestDoc);
331+
assertEquals(expectedIssueInstant, Util.formatDateTime(issueInstant.getTimeInMillis()));
332+
333+
assertNull(LogoutRequest.getIssueInstant(""));
334+
335+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build();
336+
String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request.xml.base64");
337+
final String requestURL = "http://stuff.com/endpoints/endpoints/sls.php";
338+
HttpRequest httpRequest = newHttpRequest(requestURL, samlRequestEncoded);
339+
340+
issueInstant = new LogoutRequest(settings, httpRequest).getIssueInstant();
341+
assertEquals(expectedIssueInstant, Util.formatDateTime(issueInstant.getTimeInMillis()));
342+
}
343+
344+
/**
345+
* Tests the getIssueInstant method of LogoutRequest
346+
* <p>
347+
* Case: LogoutRequest message built by the caller
348+
*
349+
* @throws Exception
350+
*
351+
* @see com.onelogin.saml2.logout.LogoutRequest#getIssueInstant(String)
352+
*/
353+
@Test
354+
public void testGetIssueInstantBuiltMessage() throws Exception {
355+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build();
356+
long start = System.currentTimeMillis();
357+
LogoutRequest logoutRequest = new LogoutRequest(settings, null);
358+
long end = System.currentTimeMillis();
359+
Calendar issueInstant = logoutRequest.getIssueInstant();
360+
assertNotNull(issueInstant);
361+
long millis = issueInstant.getTimeInMillis();
362+
assertTrue(millis >= start && millis <= end);
363+
}
364+
312365
/**
313366
* Tests the getNameIdData method of LogoutRequest
314367
* Case: Not able to get the NameIdData due no private key to decrypt

0 commit comments

Comments
 (0)