From 04b6adb42ccead7923d8e285070ad9abfe57e602 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 26 Sep 2018 15:03:28 +0200 Subject: [PATCH 001/133] check that certificate of doc matches registered certificate/fingerprint before validating signature --- .../java/com/onelogin/saml2/util/Util.java | 96 +++++++++++++++---- 1 file changed, 78 insertions(+), 18 deletions(-) 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 4c435513..47913029 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -28,10 +28,12 @@ import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; 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; import java.util.UUID; @@ -66,7 +68,9 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.exceptions.XMLSecurityException; import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.keys.keyresolver.KeyResolverException; import org.apache.xml.security.signature.XMLSignature; +import org.apache.xml.security.signature.XMLSignatureException; import org.apache.xml.security.transforms.Transforms; import org.apache.xml.security.utils.XMLUtils; import org.joda.time.DateTime; @@ -892,12 +896,32 @@ public static boolean validateSign(final Document doc, final List signatureData = getSignatureData(signNode, alg); + if (signatureData.isEmpty()) { + return false; + } + XMLSignature signature = (XMLSignature) signatureData.get("signature"); + X509Certificate extractedCert = (X509Certificate) signatureData.get("cert"); + String extractedFingerprint = (String) signatureData.get("fingerprint"); + if (certList == null || certList.isEmpty()) { - return validateSignNode(signNode, null, fingerprint, alg); + return validateSignNode(signature, null, fingerprint, alg, extractedCert, extractedFingerprint); } else { + Boolean certMatches = false; for (X509Certificate cert : certList) { - if (validateSignNode(signNode, cert, fingerprint, alg)) + if (cert != null && extractedFingerprint != null && !extractedFingerprint.equals(calculateX509Fingerprint(cert, alg))) { + continue; + } else { + certMatches = true; + } + + if (validateSignNode(signature, cert, fingerprint, alg, extractedCert, extractedFingerprint)) { return true; + } + } + if (certMatches == false) { + LOGGER.warn("Certificate used in the document does not match any registered certificate"); } } } @@ -949,6 +973,33 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S return false; } + private static Map getSignatureData(Node signNode, String alg) { + Map signatureData = new HashMap<>(); + try { + Element sigElement = (Element) signNode; + XMLSignature signature = new XMLSignature(sigElement, "", true); + + String sigMethodAlg = signature.getSignedInfo().getSignatureMethodURI(); + if (!isAlgorithmWhitelisted(sigMethodAlg)){ + throw new Exception(sigMethodAlg + " is not a valid supported algorithm"); + } + + String extractedFingerprint = null; + X509Certificate extractedCert = null; + KeyInfo keyInfo = signature.getKeyInfo(); + if (keyInfo != null && keyInfo.containsX509Data()) { + extractedCert = keyInfo.getX509Certificate(); + extractedFingerprint = calculateX509Fingerprint(extractedCert, alg); + } + signatureData.put("signature", signature); + signatureData.put("cert", extractedCert); + signatureData.put("fingerprint", extractedFingerprint); + } catch (Exception e) { + LOGGER.warn("Error executing getSignatureData: " + e.getMessage(), e); + } + return signatureData; + } + /** * Validate signature of the Node. * @@ -962,31 +1013,40 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S * The signature algorithm method * * @return True if the sign is valid, false otherwise. + * + * @throws Exception */ public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg) { - Boolean res = false; - try { - Element sigElement = (Element) signNode; - XMLSignature signature = new XMLSignature(sigElement, "", true); + Map signatureData = getSignatureData(signNode, alg); + if (signatureData.isEmpty()) { + return false; + } - String sigMethodAlg = signature.getSignedInfo().getSignatureMethodURI(); - if (!isAlgorithmWhitelisted(sigMethodAlg)){ - throw new Exception(sigMethodAlg + " is not a valid supported algorithm"); - } + XMLSignature signature = (XMLSignature) signatureData.get("signature"); + X509Certificate extractedCert = (X509Certificate) signatureData.get("cert"); + String extractedFingerprint = (String) signatureData.get("fingerprint"); + + return validateSignNode(signature, cert, fingerprint, alg, extractedCert, extractedFingerprint); + } + public static Boolean validateSignNode(XMLSignature signature, X509Certificate cert, String fingerprint, String alg, X509Certificate extractedCert, String extractedFingerprint) { + Boolean res = false; + try { if (cert != null) { res = signature.checkSignatureValue(cert); - } else { - KeyInfo keyInfo = signature.getKeyInfo(); - if (fingerprint != null && keyInfo != null && keyInfo.containsX509Data()) { - X509Certificate providedCert = keyInfo.getX509Certificate(); - String calculatedFingerprint = calculateX509Fingerprint(providedCert, alg); - for (String fingerprintStr : fingerprint.split(",")) { - if (calculatedFingerprint.equals(fingerprintStr.trim())) { - res = signature.checkSignatureValue(providedCert); + } else if (extractedCert != null && fingerprint != null && extractedFingerprint != null) { + Boolean fingerprintMatches = false; + for (String fingerprintStr : fingerprint.split(",")) { + if (extractedFingerprint.equals(fingerprintStr.trim())) { + fingerprintMatches = true; + if (res = signature.checkSignatureValue(extractedCert)) { + break; } } } + if (fingerprintMatches == false) { + LOGGER.warn("Fingerprint of the certificate used in the document does not match any registered fingerprints"); + } } } catch (Exception e) { LOGGER.warn("Error executing validateSignNode: " + e.getMessage(), e); From 5f525ac353af3adac075a9c36823c23d80cc7447 Mon Sep 17 00:00:00 2001 From: Kouroche Sedaghatian Date: Tue, 19 Feb 2019 15:00:03 -0500 Subject: [PATCH 002/133] Issue #218: Exposing statuscode and substatuscode through toolkit. --- .../onelogin/saml2/authn/SamlResponse.java | 27 ++++++++++++++----- .../main/java/com/onelogin/saml2/Auth.java | 8 ++++++ 2 files changed, 28 insertions(+), 7 deletions(-) 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 d65fe31d..a9b7c069 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -85,6 +85,11 @@ public class SamlResponse { */ private String error; + /** + * The respone status code and messages + */ + private SamlResponseStatus responseStatus; + /** * Constructor to have a Response object fully built and ready to validate the saml response. * @@ -574,19 +579,27 @@ public HashMap> getAttributes() throws XPathExpressionExcep return attributes; } + /** + * Returns the latest response status + * + * @return + */ + public SamlResponseStatus getResponseStatus() { + return this.responseStatus; + } + /** * Checks the Status * - * @throws ValidationError - * If status is not success + * @throws ValidationError If status is not success */ public void checkStatus() throws ValidationError { - SamlResponseStatus responseStatus = getStatus(samlResponseDocument); - if (!responseStatus.is(Constants.STATUS_SUCCESS)) { + this.responseStatus = getStatus(samlResponseDocument); + if (!this.responseStatus.is(Constants.STATUS_SUCCESS)) { String statusExceptionMsg = "The status code of the Response was not Success, was " - + responseStatus.getStatusCode(); - if (responseStatus.getStatusMessage() != null) { - statusExceptionMsg += " -> " + responseStatus.getStatusMessage(); + + this.responseStatus.getStatusCode(); + if (this.responseStatus.getStatusMessage() != null) { + statusExceptionMsg += " -> " + this.responseStatus.getStatusMessage(); } throw new ValidationError(statusExceptionMsg, ValidationError.STATUS_CODE_IS_NOT_SUCCESS); } diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 006aece8..da12594e 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -29,6 +29,7 @@ import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.servlet.ServletUtils; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -655,6 +656,13 @@ public void processResponse(String requestId) throws Exception { LOGGER.error("processResponse error. invalid_response"); LOGGER.debug(" --> " + samlResponseParameter); errorReason = samlResponse.getError(); + SamlResponseStatus samlResponseStatus = samlResponse.getResponseStatus(); + if (samlResponseStatus.getStatusCode() != null) { + errors.add(samlResponseStatus.getStatusCode()); + } + if (samlResponseStatus.getSubStatusCode() != null) { + errors.add(samlResponseStatus.getSubStatusCode()); + } } } else { errors.add("invalid_binding"); From 7f0f070736379f41786f4e7e1351f3d65b0641e8 Mon Sep 17 00:00:00 2001 From: Kouroche Sedaghatian Date: Wed, 20 Feb 2019 09:10:15 -0500 Subject: [PATCH 003/133] Update to make tests pass --- toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java | 2 ++ 1 file changed, 2 insertions(+) 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 586d047d..a74f02f0 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -742,6 +742,7 @@ public void testIsAuthenticated() throws Exception { assertFalse(auth.getErrors().isEmpty()); List expectedErrors = new ArrayList(); expectedErrors.add("invalid_response"); + expectedErrors.add("urn:oasis:names:tc:SAML:2.0:status:Success"); assertEquals(expectedErrors, auth.getErrors()); assertEquals("SAML Response must contain 1 Assertion.", auth.getLastErrorReason()); @@ -755,6 +756,7 @@ public void testIsAuthenticated() throws Exception { assertFalse(auth2.getErrors().isEmpty()); expectedErrors = new ArrayList(); expectedErrors.add("invalid_response"); + expectedErrors.add("urn:oasis:names:tc:SAML:2.0:status:Success"); assertEquals(expectedErrors, auth2.getErrors()); assertThat(auth2.getLastErrorReason(), containsString("Invalid issuer in the Assertion/Response")); From 54e2b57e0c410f81d15b017688337c34910d1a08 Mon Sep 17 00:00:00 2001 From: Laurent Leseigneur Date: Fri, 14 Jun 2019 14:25:46 +0200 Subject: [PATCH 004/133] fix(unit test): fix idPMetadataParserTest * seems that `try.onelogin.com` is replaced by `try-demo.onelogin.com` feat(metadata): make validUntil and cacheDuration optional * follow XSD and don't add xml attribute when value is not set Relates to [#106](https://github.com/onelogin/java-saml/issues/106) format using tabs --- .../com/onelogin/saml2/settings/Metadata.java | 225 ++++----- .../test/settings/IdPMetadataParserTest.java | 476 +++++++++--------- .../saml2/test/settings/MetadataTest.java | 154 ++++-- 3 files changed, 438 insertions(+), 417 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index fab3d227..25594c88 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -28,52 +28,47 @@ /** * Metadata class of OneLogin's Java Toolkit. - * + *

* A class that contains methods related to the metadata of the SP */ public class Metadata { /** - * Private property to construct a logger for this class. - */ + * Private property to construct a logger for this class. + */ private static final Logger LOGGER = LoggerFactory.getLogger(Metadata.class); - + // Constants private static final int N_DAYS_VALID_UNTIL = 2; private static final int SECONDS_CACHED = 604800; // 1 week /** - * AttributeConsumingService - */ + * AttributeConsumingService + */ private AttributeConsumingService attributeConsumingService = null; - + /** - * Generated metadata in string format - */ + * Generated metadata in string format + */ private final String metadataString; /** - * validUntilTime of the metadata. How long the metadata is valid - */ + * validUntilTime of the metadata. How long the metadata is valid + */ private final Calendar validUntilTime; /** - * cacheDuration of the metadata. Duration of the cache in seconds - */ + * cacheDuration of the metadata. Duration of the cache in seconds + */ private final Integer cacheDuration; /** * Constructs the Metadata object. - * - * @param settings - * Saml2Settings object. Setting data - * @param validUntilTime - * Metadata's valid time - * @param cacheDuration - * Duration of the cache in seconds - * @param attributeConsumingService - * AttributeConsumingService of service provider - * - * @throws CertificateEncodingException + * + * @param settings Saml2Settings object. Setting data + * @param validUntilTime Metadata's valid time + * @param cacheDuration Duration of the cache in seconds + * @param attributeConsumingService AttributeConsumingService of service provider + * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration, AttributeConsumingService attributeConsumingService) throws CertificateEncodingException { if (validUntilTime == null) { @@ -82,7 +77,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu } else { this.validUntilTime = validUntilTime; } - + this.attributeConsumingService = attributeConsumingService; if (cacheDuration == null) { @@ -92,53 +87,45 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu } StrSubstitutor substitutor = generateSubstitutor(settings); - String unsignedMetadataString = substitutor.replace(getMetadataTemplate()); + String unsignedMetadataString = substitutor.replace(getMetadataTemplate(validUntilTime, cacheDuration)); - LOGGER.debug("metadata --> " + unsignedMetadataString); - metadataString = unsignedMetadataString; + LOGGER.debug("metadata --> " + unsignedMetadataString); + metadataString = unsignedMetadataString; } /** * Constructs the Metadata object. - * - * @param settings - * Saml2Settings object. Setting data - * @param validUntilTime - * Metadata's valid time - * @param cacheDuration - * Duration of the cache in seconds - * - * @throws CertificateEncodingException + * + * @param settings Saml2Settings object. Setting data + * @param validUntilTime Metadata's valid time + * @param cacheDuration Duration of the cache in seconds + * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration) throws CertificateEncodingException { this(settings, validUntilTime, cacheDuration, null); } - + /** * Constructs the Metadata object. * - * @param settings - * Saml2Settings object. Setting data - * - * @throws CertificateEncodingException + * @param settings Saml2Settings object. Setting data + * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings) throws CertificateEncodingException { this(settings, null, null); } - + /** * Substitutes metadata variables within a string by values. * - * @param settings - * Saml2Settings object. Setting data - * - * @return the StrSubstitutor object of the metadata - */ + * @param settings Saml2Settings object. Setting data + * @return the StrSubstitutor object of the metadata + */ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws CertificateEncodingException { Map valueMap = new HashMap(); - Boolean wantsEncrypted = settings.getWantAssertionsEncrypted() || settings.getWantNameIdEncrypted(); - + Boolean wantsEncrypted = settings.getWantAssertionsEncrypted() || settings.getWantNameIdEncrypted(); + valueMap.put("id", Util.generateUniqueID(settings.getUniqueIDPrefix())); valueMap.put("validUntilTime", Util.formatDateTime(validUntilTime.getTimeInMillis())); valueMap.put("cacheDuration", String.valueOf(cacheDuration)); @@ -151,7 +138,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); valueMap.put("strAttributeConsumingService", getAttributeConsumingServiceXml()); - + valueMap.put("strKeyDescriptor", toX509KeyDescriptorsXML(settings.getSPcert(), wantsEncrypted)); valueMap.put("strContacts", toContactsXml(settings.getContacts())); valueMap.put("strOrganization", toOrganizationXml(settings.getOrganization())); @@ -160,15 +147,21 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif } /** + * @param validUntil + * @param cacheDuration * @return the metadata's template */ - private static StringBuilder getMetadataTemplate() { + private static StringBuilder getMetadataTemplate(Calendar validUntil, Integer cacheDuration) { StringBuilder template = new StringBuilder(); template.append(""); template.append(""); template.append(""); @@ -187,7 +180,6 @@ private static StringBuilder getMetadataTemplate() { /** * Generates the AttributeConsumingService section of the metadata's template * - * * @return the AttributeConsumingService section of the metadata's template */ private String getAttributeConsumingServiceXml() { @@ -210,10 +202,10 @@ private String getAttributeConsumingServiceXml() { String friendlyName = requestedAttribute.getFriendlyName(); String nameFormat = requestedAttribute.getNameFormat(); Boolean isRequired = requestedAttribute.isRequired(); - List attrValues = requestedAttribute.getAttributeValues() ; + List attrValues = requestedAttribute.getAttributeValues(); + + String contentStr = ""); } - + return attributeConsumingServiceXML.toString(); } - + /** * Generates the contact section of the metadata's template * - * @param contacts - * List of contact objects - * + * @param contacts List of contact objects * @return the contact section of the metadata's template */ private String toContactsXml(List contacts) { @@ -271,9 +261,8 @@ private String toContactsXml(List contacts) { /** * Generates the organization section of the metadata's template * - * @param organization - * organization object - * @return the organization section of the metadata's template + * @param organization organization object + * @return the organization section of the metadata's template */ private String toOrganizationXml(Organization organization) { String orgXml = ""; @@ -290,12 +279,9 @@ private String toOrganizationXml(Organization organization) { /** * Generates the KeyDescriptor section of the metadata's template - * - * @param cert - * the public cert that will be used by the SP to sign and encrypt - * @param wantsEncrypted - * Whether to include the KeyDescriptor for encryption * + * @param cert the public cert that will be used by the SP to sign and encrypt + * @param wantsEncrypted Whether to include the KeyDescriptor for encryption * @return the KeyDescriptor section of the metadata's template */ private String toX509KeyDescriptorsXML(X509Certificate cert, Boolean wantsEncrypted) throws CertificateEncodingException { @@ -309,7 +295,7 @@ private String toX509KeyDescriptorsXML(X509Certificate cert, Boolean wantsEncryp keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+certString+""); + keyDescriptorXml.append("" + certString + ""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); @@ -318,7 +304,7 @@ private String toX509KeyDescriptorsXML(X509Certificate cert, Boolean wantsEncryp keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+certString+""); + keyDescriptorXml.append("" + certString + ""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); @@ -330,25 +316,23 @@ private String toX509KeyDescriptorsXML(X509Certificate cert, Boolean wantsEncryp /** * Generates the KeyDescriptor section of the metadata's template - * - * @param cert - * the public cert that will be used by the SP to sign and encrypt * + * @param cert the public cert that will be used by the SP to sign and encrypt * @return the KeyDescriptor section of the metadata's template */ private String toX509KeyDescriptorsXML(X509Certificate cert) throws CertificateEncodingException { return toX509KeyDescriptorsXML(cert, true); } - + /** * @return the md:SingleLogoutService section of the metadata's template */ private String toSLSXml(URL spSingleLogoutServiceUrl, String spSingleLogoutServiceBinding) { StringBuilder slsXml = new StringBuilder(); - + if (spSingleLogoutServiceUrl != null) { - slsXml.append(""); + slsXml.append(""); } return slsXml.toString(); } @@ -360,50 +344,37 @@ public final String getMetadataString() { return metadataString; } - /** - * Signs the metadata with the key/cert provided - * - * @param metadata - * SAML Metadata XML - * @param key - * Private Key - * @param cert - * x509 Public certificate - * @param signAlgorithm - * Signature Algorithm - * - * @return string Signed Metadata - * @throws XMLSecurityException - * @throws XPathExpressionException - */ - public static String signMetadata(String metadata, PrivateKey key, X509Certificate cert, String signAlgorithm) throws XPathExpressionException, XMLSecurityException - { - return signMetadata(metadata, key, cert, signAlgorithm, Constants.SHA1); - } - - /** - * Signs the metadata with the key/cert provided - * - * @param metadata - * SAML Metadata XML - * @param key - * Private Key - * @param cert - * x509 Public certificate - * @param signAlgorithm - * Signature Algorithm - * @param digestAlgorithm - * Digest Algorithm - * - * @return string Signed Metadata - * @throws XMLSecurityException - * @throws XPathExpressionException - */ - public static String signMetadata(String metadata, PrivateKey key, X509Certificate cert, String signAlgorithm, String digestAlgorithm) throws XPathExpressionException, XMLSecurityException - { - Document metadataDoc = Util.loadXML(metadata); - String signedMetadata = Util.addSign(metadataDoc, key, cert, signAlgorithm, digestAlgorithm); - LOGGER.debug("Signed metadata --> " + signedMetadata); - return signedMetadata; - } + /** + * Signs the metadata with the key/cert provided + * + * @param metadata SAML Metadata XML + * @param key Private Key + * @param cert x509 Public certificate + * @param signAlgorithm Signature Algorithm + * @return string Signed Metadata + * @throws XMLSecurityException + * @throws XPathExpressionException + */ + public static String signMetadata(String metadata, PrivateKey key, X509Certificate cert, String signAlgorithm) throws XPathExpressionException, XMLSecurityException { + return signMetadata(metadata, key, cert, signAlgorithm, Constants.SHA1); + } + + /** + * Signs the metadata with the key/cert provided + * + * @param metadata SAML Metadata XML + * @param key Private Key + * @param cert x509 Public certificate + * @param signAlgorithm Signature Algorithm + * @param digestAlgorithm Digest Algorithm + * @return string Signed Metadata + * @throws XMLSecurityException + * @throws XPathExpressionException + */ + public static String signMetadata(String metadata, PrivateKey key, X509Certificate cert, String signAlgorithm, String digestAlgorithm) throws XPathExpressionException, XMLSecurityException { + Document metadataDoc = Util.loadXML(metadata); + String signedMetadata = Util.addSign(metadataDoc, key, cert, signAlgorithm, digestAlgorithm); + LOGGER.debug("Signed metadata --> " + signedMetadata); + return signedMetadata; + } } diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index e13f16f8..38f0ad14 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -1,238 +1,238 @@ -package com.onelogin.saml2.test.settings; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertNull; - -import java.net.URL; -import java.util.Map; - -import org.junit.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; - -import com.onelogin.saml2.settings.IdPMetadataParser; -import com.onelogin.saml2.settings.Saml2Settings; -import com.onelogin.saml2.settings.SettingsBuilder; -import com.onelogin.saml2.util.Constants; -import com.onelogin.saml2.util.Util; - -public class IdPMetadataParserTest { - - @Test - public void testParseFileXML() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/onelogin_metadata.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - boolean throwedInvalidXPath = false; - try { - IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml", null, null, "\"", "\""); - } catch (Exception e) { - throwedInvalidXPath = true; - } - assertTrue(throwedInvalidXPath); - - boolean throwedFileNotFound = false; - try { - IdPMetadataParser.parseFileXML("nonexistent.file", null, null, "\"", "\""); - } catch (Exception e) { - throwedFileNotFound = true; - } - assertTrue(throwedFileNotFound); - - } - - @Test - public void testParseXML() throws Exception { - Document xmlDocument = Util.parseXML(new InputSource(getClass().getClassLoader().getResourceAsStream("data/metadata/onelogin_metadata.xml"))); - - Map idpInfo = IdPMetadataParser.parseXML(xmlDocument); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseFileXmlMultix509cert() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + ".0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseFileXmlDesiredBindings() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml"); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", "https://idp.testshib.org/idp/shibboleth"); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_ARTIFACT, Constants.BINDING_HTTP_ARTIFACT); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/Shibboleth/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:mace:shibboleth:1.0:profiles:AuthnRequest", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseRemoteXML() throws Exception { - - Map idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/645460")); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/383123")); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://try.onelogin.com/trust/saml2/http-redirect/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://try.onelogin.com/trust/saml2/http-redirect/slo/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIDGTCCAoKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsalDL15zSKeEGy9c0Hao7+G02x6k/MlZuCwEvkPKUcl9QF/q0584lta735hmiZSuWOFQDNQ4VT53VevjAhOtzLJOsa8wcZ+SA1s3j4bNcpUIAHltb4Az6NC7U2/LatfnwscOazEJnVsfL4aaBdpIHBFQ6Ed0StD0AfB6Ci0hURwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHm3fLi+Q1zMc3guMyHy5AHdQvdgMIGRBgNVHSMEgYkwgYaAFHm3fLi+Q1zMc3guMyHy5AHdQvdgoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEANZvzlB1Aq84AdOvsn2XKxBB/PmNZLqnM1VWRPaNcvjafx7eHd5qayXFNQz+bOLujENmgAm5padbydG89SeefpOGcY2TMsVt0RUzxTnN3Zq5G6Ja2fAKOEX01ejdoPPMmStqqSw8k1wPUU8uLYJG5wmjf0rCb8RVaeAwMc+wcEIA=")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiCerts() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_certs.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiSigningCerts() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_signing_certs.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiSameSigningAndEncryptCert() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_same_sign_and_encrypt_cert.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - Map idpInfo2 = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_different_sign_and_encrypt_cert.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo2.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo2.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertNull(idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo2.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testInjectIntoSettings() throws Exception { - Saml2Settings setting = new SettingsBuilder().fromFile("config/config.min.properties").build(); - - assertEquals("http://idp.example.com/", setting.getIdpEntityId()); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting.getIdpx509cert()); - assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/FederationMetadata.xml"); - setting = IdPMetadataParser.injectIntoSettings(setting, idpInfo); - assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting.getIdpEntityId()); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); - assertEquals(setting.getIdpx509cert(), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); - assertEquals(setting.getIdpx509certMulti().get(0), Util.loadCert( - "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); - } - -} +package com.onelogin.saml2.test.settings; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.net.URL; +import java.util.Map; + +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import com.onelogin.saml2.settings.IdPMetadataParser; +import com.onelogin.saml2.settings.Saml2Settings; +import com.onelogin.saml2.settings.SettingsBuilder; +import com.onelogin.saml2.util.Constants; +import com.onelogin.saml2.util.Util; + +public class IdPMetadataParserTest { + + @Test + public void testParseFileXML() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/onelogin_metadata.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + boolean throwedInvalidXPath = false; + try { + IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml", null, null, "\"", "\""); + } catch (Exception e) { + throwedInvalidXPath = true; + } + assertTrue(throwedInvalidXPath); + + boolean throwedFileNotFound = false; + try { + IdPMetadataParser.parseFileXML("nonexistent.file", null, null, "\"", "\""); + } catch (Exception e) { + throwedFileNotFound = true; + } + assertTrue(throwedFileNotFound); + + } + + @Test + public void testParseXML() throws Exception { + Document xmlDocument = Util.parseXML(new InputSource(getClass().getClassLoader().getResourceAsStream("data/metadata/onelogin_metadata.xml"))); + + Map idpInfo = IdPMetadataParser.parseXML(xmlDocument); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseFileXmlMultix509cert() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + ".0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseFileXmlDesiredBindings() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml"); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", "https://idp.testshib.org/idp/shibboleth"); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_ARTIFACT, Constants.BINDING_HTTP_ARTIFACT); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/Shibboleth/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:mace:shibboleth:1.0:profiles:AuthnRequest", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseRemoteXML() throws Exception { + + Map idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/645460")); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/383123")); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/slo/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIDGTCCAoKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsalDL15zSKeEGy9c0Hao7+G02x6k/MlZuCwEvkPKUcl9QF/q0584lta735hmiZSuWOFQDNQ4VT53VevjAhOtzLJOsa8wcZ+SA1s3j4bNcpUIAHltb4Az6NC7U2/LatfnwscOazEJnVsfL4aaBdpIHBFQ6Ed0StD0AfB6Ci0hURwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHm3fLi+Q1zMc3guMyHy5AHdQvdgMIGRBgNVHSMEgYkwgYaAFHm3fLi+Q1zMc3guMyHy5AHdQvdgoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEANZvzlB1Aq84AdOvsn2XKxBB/PmNZLqnM1VWRPaNcvjafx7eHd5qayXFNQz+bOLujENmgAm5padbydG89SeefpOGcY2TMsVt0RUzxTnN3Zq5G6Ja2fAKOEX01ejdoPPMmStqqSw8k1wPUU8uLYJG5wmjf0rCb8RVaeAwMc+wcEIA=")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiCerts() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_certs.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiSigningCerts() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_signing_certs.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiSameSigningAndEncryptCert() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_same_sign_and_encrypt_cert.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + Map idpInfo2 = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_different_sign_and_encrypt_cert.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo2.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo2.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String) idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertNull(idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo2.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testInjectIntoSettings() throws Exception { + Saml2Settings setting = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + assertEquals("http://idp.example.com/", setting.getIdpEntityId()); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting.getIdpx509cert()); + assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/FederationMetadata.xml"); + setting = IdPMetadataParser.injectIntoSettings(setting, idpInfo); + assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting.getIdpEntityId()); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + assertEquals(setting.getIdpx509cert(), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); + assertEquals(setting.getIdpx509certMulti().get(0), Util.loadCert( + "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); + } + +} diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java index e1da4a68..5a11cf6e 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java @@ -1,6 +1,7 @@ package com.onelogin.saml2.test.settings; +import static junit.framework.TestCase.assertNull; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; @@ -12,7 +13,6 @@ import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Calendar; @@ -43,7 +43,6 @@ public class MetadataTest { * Tests the constructor method of Metadata * * @throws Exception - * * @see com.onelogin.saml2.settings.Metadata */ @Test @@ -67,17 +66,16 @@ public void testMetadata() throws Exception { assertThat(metadataStr, containsString("WantAssertionsSigned=\"false\"")); assertThat(metadataStr, not(containsString(""))); assertThat(metadataStr, containsString("")); - assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("")); assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } /** * Tests the constructor method of Metadata (Expiration) - * - * @throws IOException - * @throws CertificateEncodingException - * @throws Error * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error * @see com.onelogin.saml2.settings.Metadata */ @Test @@ -89,32 +87,31 @@ public void testMetadataExpiration() throws IOException, CertificateEncodingExce Calendar validUntilTime = Calendar.getInstance(); validUntilTime.add(Calendar.DAY_OF_YEAR, 2); - String validUntilStr = "validUntil=\"" + Util.formatDateTime(validUntilTime.getTimeInMillis()) + "\""; + String validUntilStr = "validUntil=\"" + Util.formatDateTime(validUntilTime.getTimeInMillis()) + "\""; - assertThat(metadataStr, containsString("cacheDuration=\"PT604800S\"")); - assertThat(metadataStr, containsString(validUntilStr)); + assertThat(metadataStr, not(containsString("cacheDuration"))); + assertThat(metadataStr, not(containsString(validUntilStr))); validUntilTime.add(Calendar.DAY_OF_YEAR, 2); - String validUntilStr2 = "validUntil=\"" + Util.formatDateTime(validUntilTime.getTimeInMillis()) + "\""; + String validUntilStr2 = "validUntil=\"" + Util.formatDateTime(validUntilTime.getTimeInMillis()) + "\""; Metadata metadataObj2 = new Metadata(settings, validUntilTime, 36000); String metadataStr2 = metadataObj2.getMetadataString(); assertThat(metadataStr2, containsString("cacheDuration=\"PT36000S\"")); assertThat(metadataStr2, containsString(validUntilStr2)); } - + /** * Tests the toContactsXml method of Metadata * * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toContactsXml */ @Test public void testToContactsXml() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); @@ -124,7 +121,7 @@ public void testToContactsXml() throws IOException, CertificateEncodingException Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); Metadata metadataObj2 = new Metadata(settings2); String metadataStr2 = metadataObj2.getMetadataString(); - + assertThat(metadataStr2, not(containsString(contactStr))); } @@ -134,7 +131,6 @@ public void testToContactsXml() throws IOException, CertificateEncodingException * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toOrganizationXml */ @Test @@ -142,7 +138,7 @@ public void testToOrganizationXml() throws IOException, CertificateEncodingExcep Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); - + String orgStr = "SP JavaSP Java Examplehttp://sp.example.com"; assertThat(metadataStr, containsString(orgStr)); @@ -152,14 +148,13 @@ public void testToOrganizationXml() throws IOException, CertificateEncodingExcep assertThat(metadataStr2, not(containsString(orgStr))); } - + /** * Tests the toOrganizationXml method of Metadata without any "lang" attribute * * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toOrganizationXml */ @Test @@ -167,7 +162,7 @@ public void testToNonLocalizedOrganizationXml() throws IOException, CertificateE Saml2Settings settings = new SettingsBuilder().fromFile("config/config.org.properties").build(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); - + String orgStr = "SP JavaSP Java Examplehttp://sp.example.com"; assertThat(metadataStr, containsString(orgStr)); @@ -178,14 +173,13 @@ public void testToNonLocalizedOrganizationXml() throws IOException, CertificateE assertThat(metadataStr2, not(containsString(orgStr))); } - + /** * Tests the toOrganizationXml method of Metadata using a non default "lang" attribute * * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toOrganizationXml */ @Test @@ -193,7 +187,7 @@ public void testToLocalizedOrganizationXml() throws IOException, CertificateEnco Saml2Settings settings = new SettingsBuilder().fromFile("config/config.org.localized.properties").build(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); - + String orgStr = "SP JavaSP Exemple Javahttp://sp.example.com/fr"; assertThat(metadataStr, containsString(orgStr)); @@ -210,12 +204,11 @@ public void testToLocalizedOrganizationXml() throws IOException, CertificateEnco * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toSLSXml */ @Test public void testToSLSXml() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); @@ -236,12 +229,11 @@ public void testToSLSXml() throws IOException, CertificateEncodingException, Err * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toX509KeyDescriptorsXML */ @Test public void testToX509KeyDescriptorsXML() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); @@ -266,12 +258,11 @@ public void testToX509KeyDescriptorsXML() throws IOException, CertificateEncodin * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#toX509KeyDescriptorsXML */ @Test public void testToX509KeyDescriptorsXMLEncryption() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); String keyDescriptorEncStr = "MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czET"; settings.setWantAssertionsEncrypted(false); @@ -298,32 +289,31 @@ public void testToX509KeyDescriptorsXMLEncryption() throws IOException, Certific metadataStr = metadataObj.getMetadataString(); assertThat(metadataStr, containsString(keyDescriptorEncStr)); } - + /** * Tests the getAttributeConsumingServiceXml method of Metadata - * + * * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml */ @Test public void testGetAttributeConsumingServiceXml() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); - + Saml2Settings settings = getSettingFromAllProperties(); + AttributeConsumingService attributeConsumingService = new AttributeConsumingService("Test Service", "Test Service Desc"); RequestedAttribute requestedAttribute = new RequestedAttribute("Email", "Email", true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); RequestedAttribute requestedAttribute2 = new RequestedAttribute("FirstName", null, true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); RequestedAttribute requestedAttribute3 = new RequestedAttribute("LastName", null, true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); - + attributeConsumingService.addRequestedAttribute(requestedAttribute); attributeConsumingService.addRequestedAttribute(requestedAttribute2); attributeConsumingService.addRequestedAttribute(requestedAttribute3); - + Metadata metadataObj = new Metadata(settings, null, null, attributeConsumingService); String metadataStr = metadataObj.getMetadataString(); - + String headerStr = ""; String sNameStr = "Test Service"; String sDescStr = "Test Service Desc"; @@ -343,31 +333,30 @@ public void testGetAttributeConsumingServiceXml() throws IOException, Certificat /** * Tests the getAttributeConsumingServiceXml method of Metadata - * Case: AttributeConsumingService Multiple AttributeValue - * + * Case: AttributeConsumingService Multiple AttributeValue + * * @throws IOException * @throws CertificateEncodingException * @throws Error - * * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml */ @Test public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); - + Saml2Settings settings = getSettingFromAllProperties(); + AttributeConsumingService attributeConsumingService = new AttributeConsumingService("Test Service", "Test Service Desc"); List attrValues = new ArrayList(); attrValues.add("userType"); - attrValues.add("admin"); + attrValues.add("admin"); RequestedAttribute requestedAttribute = new RequestedAttribute("userType", null, false, "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", attrValues); RequestedAttribute requestedAttribute2 = new RequestedAttribute("urn:oid:0.9.2342.19200300.100.1.1", "uid", true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); attributeConsumingService.addRequestedAttribute(requestedAttribute); attributeConsumingService.addRequestedAttribute(requestedAttribute2); - + Metadata metadataObj = new Metadata(settings, null, null, attributeConsumingService); String metadataStr = metadataObj.getMetadataString(); - + String headerStr = ""; String sNameStr = "Test Service"; String sDescStr = "Test Service Desc"; @@ -395,7 +384,6 @@ public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() thro * @throws GeneralSecurityException * @throws XMLSecurityException * @throws XPathExpressionException - * * @see com.onelogin.saml2.settings.Metadata#signMetadata */ @Test @@ -424,7 +412,7 @@ public void testSignImportedMetadata() throws IOException, GeneralSecurityExcept assertEquals("ds:DigestMethod", digest_method_metadata_signed.getNodeName()); assertEquals(digestAlgorithmSha1, digest_method_metadata_signed.getAttributes().getNamedItem("Algorithm").getNodeValue()); } - + /** * Tests the signMetadata method of Metadata * Case generated metadata @@ -432,21 +420,20 @@ public void testSignImportedMetadata() throws IOException, GeneralSecurityExcept * @throws Error * @throws IOException * @throws GeneralSecurityException - * @throws XMLSecurityException + * @throws XMLSecurityException * @throws XPathExpressionException - * * @see com.onelogin.saml2.settings.Metadata#signMetadata */ @Test public void testSigngeneratedMetadata() throws Error, IOException, GeneralSecurityException, XPathExpressionException, XMLSecurityException { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); String certString = Util.getFileAsString("data/customPath/certs/sp.crt"); X509Certificate cert = Util.loadCert(certString); String keyString = Util.getFileAsString("data/customPath/certs/sp.pem"); - PrivateKey key = Util.loadPrivateKey(keyString); + PrivateKey key = Util.loadPrivateKey(keyString); String signAlgorithmSha256 = Constants.RSA_SHA256; String digestAlgorithmSha512 = Constants.SHA512; - + Metadata metadataObj = new Metadata(settings); String metadata = metadataObj.getMetadataString(); String metadataSigned = Metadata.signMetadata(metadata, key, cert, signAlgorithmSha256, digestAlgorithmSha512); @@ -465,4 +452,67 @@ public void testSigngeneratedMetadata() throws Error, IOException, GeneralSecuri assertEquals("ds:DigestMethod", digest_method_metadata_signed.getNodeName()); assertEquals(digestAlgorithmSha512, digest_method_metadata_signed.getAttributes().getNamedItem("Algorithm").getNodeValue()); } + + private Saml2Settings getSettingFromAllProperties() throws Error { + return new SettingsBuilder().fromFile("config/config.all.properties").build(); + } + + @Test + public void shouldIncludeValidUntilAndDuration() throws CertificateEncodingException, Error { + //given + Saml2Settings saml2Settings = getSettingFromAllProperties(); + + //when + Integer cacheDuration = 123; + Calendar validUntil = Calendar.getInstance(); + Metadata metadata = new Metadata(saml2Settings, validUntil, cacheDuration); + String metadataString = metadata.getMetadataString(); + + //then + Document metadataSignedDoc = Util.loadXML(metadataString); + Node validUntilNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("validUntil"); + Node cacheDurationNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("cacheDuration"); + assertEquals("should set valid until attribute", Util.formatDateTime(validUntil.getTimeInMillis()), validUntilNode.getTextContent()); + assertEquals("should set cache duration attribute", "PT123S", cacheDurationNode.getTextContent()); + + } + + @Test + public void shouldIgnoreValidUntil() throws CertificateEncodingException, Error { + //given + Saml2Settings saml2Settings = getSettingFromAllProperties(); + + //when + Integer cacheDuration = 123; + Calendar validUntil = Calendar.getInstance(); + Metadata metadata = new Metadata(saml2Settings, null, cacheDuration); + String metadataString = metadata.getMetadataString(); + + //then + Document metadataSignedDoc = Util.loadXML(metadataString); + Node validUntilNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("validUntil"); + Node cacheDurationNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("cacheDuration"); + assertNull("should not set valid until attribute", validUntilNode); + assertEquals("should set cache duration attribute", "PT123S", cacheDurationNode.getTextContent()); + } + + @Test + public void shouldIgnoreCacheDuration() throws CertificateEncodingException, Error { + //given + Saml2Settings saml2Settings = getSettingFromAllProperties(); + + //when + Integer cacheDuration = 123; + Calendar validUntil = Calendar.getInstance(); + Metadata metadata = new Metadata(saml2Settings, validUntil, null); + String metadataString = metadata.getMetadataString(); + + //then + Document metadataSignedDoc = Util.loadXML(metadataString); + Node validUntilNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("validUntil"); + Node cacheDurationNode = metadataSignedDoc.getFirstChild().getAttributes().getNamedItem("cacheDuration"); + assertEquals("should set valid until attribute", Util.formatDateTime(validUntil.getTimeInMillis()), validUntilNode.getTextContent()); + assertNull("should not set cache duration attribute", cacheDurationNode); + + } } From 2af62ba64e643b09ceaf8723a1dfc2bbbbf2ce8c Mon Sep 17 00:00:00 2001 From: jzuriaga Date: Wed, 4 Dec 2019 19:53:56 +0100 Subject: [PATCH 005/133] Allow duplicated names in AttributeStatement by configuration. --- .../onelogin/saml2/authn/SamlResponse.java | 18 ++- .../saml2/settings/Saml2Settings.java | 18 +++ .../saml2/settings/SettingsBuilder.java | 7 +- .../saml2/test/authn/AuthnResponseTest.java | 92 +++++++----- .../config.allowduplicatednames.properties | 139 ++++++++++++++++++ 5 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 core/src/test/resources/config/config.allowduplicatednames.properties 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 d65fe31d..3da93c66 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -8,10 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; - import org.joda.time.DateTime; import org.joda.time.Instant; import org.slf4j.Logger; @@ -22,7 +20,8 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; - +import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.model.SubjectConfirmationIssue; @@ -31,9 +30,6 @@ import com.onelogin.saml2.util.SchemaFactory; import com.onelogin.saml2.util.Util; -import com.onelogin.saml2.exception.SettingsException; -import com.onelogin.saml2.exception.ValidationError; - /** * SamlResponse class of OneLogin's Java Toolkit. * @@ -553,18 +549,24 @@ public HashMap> getAttributes() throws XPathExpressionExcep for (int i = 0; i < nodes.getLength(); i++) { NamedNodeMap attrName = nodes.item(i).getAttributes(); String attName = attrName.getNamedItem("Name").getNodeValue(); - if (attributes.containsKey(attName)) { + if (attributes.containsKey(attName) && !settings.isSpAllowRepeatAttributeName()) { throw new ValidationError("Found an Attribute element with duplicated Name", ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND); } NodeList childrens = nodes.item(i).getChildNodes(); - List attrValues = new ArrayList(); + List attrValues = null; + if (attributes.containsKey(attName) && settings.isSpAllowRepeatAttributeName()) { + attrValues = attributes.get(attName); + } else { + attrValues = new ArrayList(); + } for (int j = 0; j < childrens.getLength(); j++) { if ("AttributeValue".equals(childrens.item(j).getLocalName())) { attrValues.add(childrens.item(j).getTextContent()); } } + attributes.put(attName, attrValues); } LOGGER.debug("SAMLResponse has attributes: " + attributes.toString()); diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index fd0feacf..94365e4f 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -39,6 +39,7 @@ public class Saml2Settings { private URL spSingleLogoutServiceUrl = null; private String spSingleLogoutServiceBinding = Constants.BINDING_HTTP_REDIRECT; private String spNameIDFormat = Constants.NAMEID_UNSPECIFIED; + private boolean spAllowRepeatAttributeName = false; private X509Certificate spX509cert = null; private X509Certificate spX509certNew = null; private PrivateKey spPrivateKey = null; @@ -133,6 +134,13 @@ public final String getSpNameIDFormat() { return spNameIDFormat; } + /** + * @return the spAllowRepeatAttributeName setting value + */ + public boolean isSpAllowRepeatAttributeName () { + return spAllowRepeatAttributeName; + } + /** * @return the spX509cert setting value */ @@ -441,6 +449,16 @@ protected final void setSpNameIDFormat(String spNameIDFormat) { this.spNameIDFormat = spNameIDFormat; } + /** + * Set the spAllowRepeatAttributeName setting value + * + * @param spAllowRepeatAttributeName + * the spAllowRepeatAttributeName value to be set + */ + public void setSpAllowRepeatAttributeName (boolean spAllowRepeatAttributeName) { + this.spAllowRepeatAttributeName = spAllowRepeatAttributeName; + } + /** * Set the spX509cert setting value provided as X509Certificate object * diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 5eec077e..ad92036c 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -20,11 +20,9 @@ import java.util.List; import java.util.Map; import java.util.Properties; - import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; @@ -62,6 +60,7 @@ public class SettingsBuilder { public final static String SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY = "onelogin.saml2.sp.single_logout_service.url"; public final static String SP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY = "onelogin.saml2.sp.single_logout_service.binding"; public final static String SP_NAMEIDFORMAT_PROPERTY_KEY = "onelogin.saml2.sp.nameidformat"; + public final static String SP_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.sp.allow_duplicated_attribute_name"; public final static String SP_X509CERT_PROPERTY_KEY = "onelogin.saml2.sp.x509cert"; public final static String SP_PRIVATEKEY_PROPERTY_KEY = "onelogin.saml2.sp.privatekey"; @@ -470,6 +469,10 @@ private void loadSpSetting() { if (spNameIDFormat != null && !spNameIDFormat.isEmpty()) saml2Setting.setSpNameIDFormat(spNameIDFormat); + Boolean spAllowRepeatAttributeName = loadBooleanProperty(SP_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY); + if (spAllowRepeatAttributeName != null) + saml2Setting.setSpAllowRepeatAttributeName(spAllowRepeatAttributeName); + boolean keyStoreEnabled = this.samlData.get(KEYSTORE_KEY) != null && this.samlData.get(KEYSTORE_ALIAS) != null && this.samlData.get(KEYSTORE_KEY_PASSWORD) != null; 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 dfba04eb..3ff192f7 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 @@ -1,26 +1,15 @@ package com.onelogin.saml2.test.authn; -import com.onelogin.saml2.authn.SamlResponse; -import com.onelogin.saml2.exception.Error; -import com.onelogin.saml2.exception.SettingsException; -import com.onelogin.saml2.exception.ValidationError; -import com.onelogin.saml2.http.HttpRequest; -import com.onelogin.saml2.model.SamlResponseStatus; -import com.onelogin.saml2.settings.Saml2Settings; -import com.onelogin.saml2.settings.SettingsBuilder; -import com.onelogin.saml2.util.Constants; -import com.onelogin.saml2.util.Util; - -import org.hamcrest.Matchers; -import org.joda.time.Instant; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -31,19 +20,27 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; - import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import org.hamcrest.Matchers; +import org.joda.time.Instant; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import com.onelogin.saml2.authn.SamlResponse; +import com.onelogin.saml2.exception.Error; +import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.exception.ValidationError; +import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.model.SamlResponseStatus; +import com.onelogin.saml2.settings.Saml2Settings; +import com.onelogin.saml2.settings.SettingsBuilder; +import com.onelogin.saml2.util.Constants; +import com.onelogin.saml2.util.Util; public class AuthnResponseTest { private static final String ACS_URL = "http://localhost:8080/java-saml-jspsample/acs.jsp"; @@ -982,11 +979,38 @@ public void testGetAttributesDuplicatedNames() throws IOException, Error, XPathE samlResponse.getAttributes(); } + /** + * Tests the getAttributes method of SamlResponse + * Case: Allow Duplicated names + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getAttributes + */ + @Test + public void testGetAttributesAllowDuplicatedNames () throws IOException, Error, XPathExpressionException, ParserConfigurationException, + SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.allowduplicatednames.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/duplicated_attributes.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + + Map> attributes = samlResponse.getAttributes(); + assertNotNull(attributes); + assertTrue(attributes.containsKey("uid")); + assertEquals(2, attributes.get("uid").size()); + } + /** * Tests that queryAssertion method of SamlResponse - * Case: Elements retrieved are covered by a Signature + * Case: Elements retrieved are covered by a Signature * - * @throws Exception + * @throws Exception * * @see com.onelogin.saml2.authn.SamlResponse#queryAssertion */ diff --git a/core/src/test/resources/config/config.allowduplicatednames.properties b/core/src/test/resources/config/config.allowduplicatednames.properties new file mode 100644 index 00000000..4cfec295 --- /dev/null +++ b/core/src/test/resources/config/config.allowduplicatednames.properties @@ -0,0 +1,139 @@ +# If 'strict' is True, then the Java Toolkit will reject unsigned +# or unencrypted messages if it expects them signed or encrypted +# Also will reject the messages if not strictly follow the SAML +onelogin.saml2.strict = true + +# Enable debug mode (to print errors) +onelogin.saml2.debug = true + +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp +# SAML protocol binding to be used when returning the or sending the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-POST binding only +onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp + +# SAML protocol binding for the Single Logout Service of the SP. +# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Specifies constraints on the name identifier to be used to +# represent the requested subject. +# Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported +onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + +# Enable duplicated names in the attribute statement +onelogin.saml2.sp.allow_duplicated_attribute_name = true + +# Usually x509cert and privateKey of the SP are provided by files placed at +# the certs folder. But we can also provide them with the following parameters +onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- + + +# Requires Format PKCS#8 BEGIN PRIVATE KEY +# If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem +onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9LcGorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6wsKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786UlujTvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXBBgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6RnfdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXzVYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRXEouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKCUn0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x9+QU/vJr7lMLzdoy-----END PRIVATE KEY----- + +# Identity Provider Data that we want connect with our SP +# Identifier of the IdP entity (must be a URI) +onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php + +# SSO endpoint info of the IdP. (Authentication Request protocol) +# URL Target of the IdP where the SP will send the Authentication Request Message +onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php + +# SAML protocol binding to be used when returning the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# SLO endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Request +onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php + +# SAML protocol binding to be used when returning the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Public x509 certificate of the IdP +onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- + +# Security settings +# + +# Indicates that the nameID of the sent by this SP +# will be encrypted. +onelogin.saml2.security.nameid_encrypted = true + +# Indicates whether the messages sent by this SP +# will be signed. [The Metadata of the SP will offer this info] +onelogin.saml2.security.authnrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutresponse_signed = true + +# Indicates a requirement for the , and +# elements received by this SP to be signed. +onelogin.saml2.security.want_messages_signed = true + +# Indicates a requirement for the of the to be signed +onelogin.saml2.security.want_assertions_signed = true + +# Indicates a requirement for the Metadata of this SP to be signed. +# Right now supported null/false (in order to not sign) or true (sign using SP private key) +onelogin.saml2.security.sign_metadata = true + +# Indicates a requirement for the Assertions received by this SP to be encrypted +onelogin.saml2.security.want_assertions_encrypted = false + +# Indicates a requirement for the NameID received by this SP to be encrypted +onelogin.saml2.security.want_nameid = true + +# Indicates a requirement for the NameID received by this SP to be encrypted +onelogin.saml2.security.want_nameid_encrypted = false + +# Authentication context. +# Set Empty and no AuthContext will be sent in the AuthNRequest, +# Set comma separated values urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password +onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password + +# Allows the authn comparison parameter to be set, defaults to 'exact' +onelogin.saml2.security.requested_authncontextcomparison = exact + + +# Indicates if the SP will validate all received xmls. +# (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). +onelogin.saml2.security.want_xml_validation = true + +# Algorithm that the toolkit will use on signing process. Options: +# 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' +onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 + +# Organization +onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.displayname = SP Java Example +onelogin.saml2.organization.url = http://sp.example.com + +# Contacts +onelogin.saml2.contacts.technical.given_name = Technical Guy +onelogin.saml2.contacts.technical.email_address = technical@example.com +onelogin.saml2.contacts.support.given_name = Support Guy +onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file From 2886c715486a5832f819f7c34d37e74a4cb865ef Mon Sep 17 00:00:00 2001 From: Pete Nattress Date: Wed, 19 Feb 2020 17:23:19 +0000 Subject: [PATCH 006/133] Expose validation exception in SamlResponse --- .../onelogin/saml2/authn/SamlResponse.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) 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 d65fe31d..7dbec93d 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -83,7 +83,7 @@ public class SamlResponse { /** * After validation, if it fails this property has the cause of the problem */ - private String error; + private Exception validationException; /** * Constructor to have a Response object fully built and ready to validate the saml response. @@ -148,7 +148,7 @@ public void loadXmlFromBase64(String responseStr) throws ParserConfigurationExce * @return if the response is valid or not */ public boolean isValid(String requestId) { - error = null; + validationException = null; try { if (samlResponseDocument == null) { @@ -311,9 +311,9 @@ public boolean isValid(String requestId) { LOGGER.debug("SAMLResponse validated --> {}", samlResponseString); return true; } catch (Exception e) { - error = e.getMessage(); + validationException = e; LOGGER.debug("SAMLResponse invalid --> {}", samlResponseString); - LOGGER.error(error); + LOGGER.error(validationException.getMessage()); return false; } } @@ -964,15 +964,24 @@ public void setDestinationUrl(String url) { /** * After execute a validation process, if fails this method returns the cause * - * @return the cause of the validation error + * @return the cause of the validation error as a string */ public String getError() { - if (error != null) { - return error; + if (validationException != null) { + return validationException.getMessage(); } return null; } + /** + * After execute a validation process, if fails this method returns the cause + * + * @return the cause of the validation error + */ + public Exception getValidationException() { + return validationException; + } + /** * Extracts a node from the DOMDocument (Assertion). * From 56cea3f78e462b42940b09f3da3ef231cb87d6a4 Mon Sep 17 00:00:00 2001 From: Pete Nattress Date: Fri, 21 Feb 2020 14:41:43 +0000 Subject: [PATCH 007/133] Expose validation exceptions in Auth, LogoutRequest and LogoutResponse Also add unit tests for validation exceptions --- .../onelogin/saml2/authn/SamlResponse.java | 2 +- .../onelogin/saml2/logout/LogoutRequest.java | 23 +++++++++++++++---- .../onelogin/saml2/logout/LogoutResponse.java | 22 ++++++++++++++---- .../saml2/test/authn/AuthnResponseTest.java | 9 +++++++- .../saml2/test/logout/LogoutRequestTest.java | 6 ++++- .../saml2/test/logout/LogoutResponseTest.java | 7 +++++- .../main/java/com/onelogin/saml2/Auth.java | 15 ++++++++++++ .../com/onelogin/saml2/test/AuthTest.java | 4 ++++ 8 files changed, 74 insertions(+), 14 deletions(-) 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 7dbec93d..575fe14e 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -974,7 +974,7 @@ public String getError() { } /** - * After execute a validation process, if fails this method returns the cause + * After execute a validation process, if fails this method returns the Exception object * * @return the cause of the validation error */ 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 e045d1ad..a9431136 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -98,7 +98,7 @@ public class LogoutRequest { /** * After validation, if it fails this property has the cause of the problem */ - private String error; + private Exception validationException; /** * Constructs the LogoutRequest object. @@ -366,7 +366,7 @@ private static StringBuilder getLogoutRequestTemplate() { * @throws Exception */ public Boolean isValid() throws Exception { - error = null; + validationException = null; try { if (this.logoutRequestString == null || logoutRequestString.isEmpty()) { @@ -474,9 +474,9 @@ public Boolean isValid() throws Exception { LOGGER.debug("LogoutRequest validated --> " + logoutRequestString); return true; } catch (Exception e) { - error = e.getMessage(); + validationException = e; LOGGER.debug("LogoutRequest invalid --> " + logoutRequestString); - LOGGER.error(error); + LOGGER.error(validationException.getMessage()); return false; } } @@ -737,9 +737,22 @@ public static List getSessionIndexes(String samlLogoutRequestString) thr * @return the cause of the validation error */ public String getError() { - return error; + if (validationException != null) { + return validationException.getMessage(); + } + return null; } + /** + * After execute a validation process, if fails this method returns the Exception object + * + * @return the cause of the validation error + */ + public Exception getValidationException() { + return validationException; + } + + /** * @return the ID of the Logout Request */ 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 1cba0b5b..b89c522d 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -82,7 +82,7 @@ public class LogoutResponse { /** * After validation, if it fails this property has the cause of the problem */ - private String error; + private Exception validationException; /** * Constructs the LogoutResponse object. @@ -168,7 +168,7 @@ public String getId() { * @return if the SAML LogoutResponse is or not valid */ public Boolean isValid(String requestId) { - error = null; + validationException = null; try { if (this.logoutResponseDocument == null) { @@ -270,9 +270,9 @@ public Boolean isValid(String requestId) { LOGGER.debug("LogoutRequest validated --> " + logoutResponseString); return true; } catch (Exception e) { - error = e.getMessage(); + validationException = e; LOGGER.debug("LogoutResponse invalid --> " + logoutResponseString); - LOGGER.error(error); + LOGGER.error(validationException.getMessage()); return false; } } @@ -451,6 +451,18 @@ private static StringBuilder getLogoutResponseTemplate() { * @return the cause of the validation error */ public String getError() { - return error; + if (validationException != null) { + return validationException.getMessage(); + } + return null; + } + + /** + * After execute a validation process, if fails this method returns the Exception object + * + * @return the cause of the validation error + */ + public Exception getValidationException() { + return validationException; } } 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 dfba04eb..70ee99dd 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 @@ -2804,7 +2804,7 @@ public void testIsValidSignWithEmptyReferenceURI() throws IOException, Error, XP } /** - * Tests the getError method of SamlResponse + * Tests the getError and getValidationException methods of SamlResponse * * @throws ValidationError * @throws SettingsException @@ -2823,25 +2823,32 @@ public void testGetError() throws IOException, Error, XPathExpressionException, String samlResponseEncoded = Util.getFileAsString("data/responses/response4.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertNull(samlResponse.getError()); + assertNull(samlResponse.getValidationException()); samlResponse.isValid(); assertThat(samlResponse.getError(), containsString("SAML Response must contain 1 Assertion.")); + assertTrue(samlResponse.getValidationException() instanceof ValidationError); settings.setStrict(false); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); samlResponse.isValid(); assertThat(samlResponse.getError(), containsString("SAML Response must contain 1 Assertion.")); + assertTrue(samlResponse.getValidationException() instanceof ValidationError); samlResponseEncoded = Util.getFileAsString("data/responses/valid_response.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertNull(samlResponse.getError()); + assertNull(samlResponse.getValidationException()); samlResponse.isValid(); assertNull(samlResponse.getError()); + assertNull(samlResponse.getValidationException()); settings.setStrict(true); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertNull(samlResponse.getError()); + assertNull(samlResponse.getValidationException()); samlResponse.isValid(); assertNull(samlResponse.getError()); + assertNull(samlResponse.getValidationException()); } private String loadAndEncode(String path) throws Exception diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index ba1821db..78f4eac0 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -812,7 +812,7 @@ public void testIsValidNoCurrentURL() throws Exception { } /** - * Tests the getError method of LogoutRequest + * Tests the getError and getValidationException methods of LogoutRequest * * @throws Exception * @@ -828,14 +828,18 @@ public void testGetError() throws Exception { LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); assertNull(logoutRequest.getError()); + assertNull(logoutRequest.getValidationException()); logoutRequest.isValid(); assertThat(logoutRequest.getError(), containsString("The LogoutRequest was received at")); + assertTrue(logoutRequest.getValidationException() instanceof ValidationError); settings.setStrict(false); logoutRequest = new LogoutRequest(settings, httpRequest); assertNull(logoutRequest.getError()); + assertNull(logoutRequest.getValidationException()); logoutRequest.isValid(); assertNull(logoutRequest.getError()); + assertNull(logoutRequest.getValidationException()); } private static HttpRequest newHttpRequest(String requestURL, String samlRequestEncoded) { diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index 8a6cfb5f..67afc6c7 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -9,6 +9,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import com.onelogin.saml2.exception.ValidationError; import java.io.IOException; import java.net.URISyntaxException; @@ -573,7 +574,7 @@ public void testIsValidNoLogoutResponse() throws IOException, XMLEntityException } /** - * Tests the getError method of LogoutResponse + * Tests the getError and getValidationException methods of LogoutResponse * * @throws IOException * @throws URISyntaxException @@ -591,14 +592,18 @@ public void testGetError() throws URISyntaxException, IOException, XMLEntityExce HttpRequest httpRequest = newHttpRequest(requestURL, samlResponseEncoded); LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); assertNull(logoutResponse.getError()); + assertNull(logoutResponse.getValidationException()); logoutResponse.isValid(); assertThat(logoutResponse.getError(), containsString("The LogoutResponse was received at")); + assertTrue(logoutResponse.getValidationException() instanceof ValidationError); settings.setStrict(false); logoutResponse = new LogoutResponse(settings, httpRequest); assertNull(logoutResponse.getError()); + assertNull(logoutResponse.getValidationException()); logoutResponse.isValid(); assertNull(logoutResponse.getError()); + assertNull(logoutResponse.getValidationException()); } private static HttpRequest newHttpRequest(String requestURL, String samlResponseEncoded) { diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index bf7073e9..e131b245 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -133,6 +133,11 @@ public class Auth { */ private String errorReason; + /** + * Exception of the last error. + */ + private Exception validationException; + /** * The id of the last request (Authn or Logout) generated */ @@ -748,6 +753,7 @@ public void processResponse(String requestId) throws Exception { LOGGER.error("processResponse error. invalid_response"); LOGGER.debug(" --> " + samlResponseParameter); errorReason = samlResponse.getError(); + validationException = samlResponse.getValidationException(); } } else { errors.add("invalid_binding"); @@ -790,6 +796,7 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except LOGGER.error("processSLO error. invalid_logout_response"); LOGGER.debug(" --> " + samlResponseParameter); errorReason = logoutResponse.getError(); + validationException = logoutResponse.getValidationException(); } else { String status = logoutResponse.getStatus(); if (status == null || !status.equals(Constants.STATUS_SUCCESS)) { @@ -812,6 +819,7 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except LOGGER.error("processSLO error. invalid_logout_request"); LOGGER.debug(" --> " + samlRequestParameter); errorReason = logoutRequest.getError(); + validationException = logoutRequest.getValidationException(); } else { lastMessageId = logoutRequest.getId(); LOGGER.debug("processSLO success --> " + samlRequestParameter); @@ -972,6 +980,13 @@ public String getLastErrorReason() { return errorReason; } + /** + * @return the exception for the last error + */ + public Exception getLastValidationException() { + return validationException; + } + /** * @return the id of the last request generated (AuthnRequest or LogoutRequest), * null if none 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 3c868db1..113e3609 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -713,6 +713,7 @@ public void testProcessSLORequestInvalid() throws Exception { assertFalse(auth.getErrors().isEmpty()); assertTrue(auth.getErrors().contains("invalid_logout_request")); assertThat(auth.getLastErrorReason(), containsString("The LogoutRequest was received at")); + assertTrue(auth.getLastValidationException() instanceof ValidationError); } /** @@ -854,6 +855,7 @@ public void testIsAuthenticated() throws Exception { expectedErrors.add("invalid_response"); assertEquals(expectedErrors, auth.getErrors()); assertEquals("SAML Response must contain 1 Assertion.", auth.getLastErrorReason()); + assertTrue(auth.getLastValidationException() instanceof ValidationError); samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64"); when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded})); @@ -867,6 +869,7 @@ public void testIsAuthenticated() throws Exception { expectedErrors.add("invalid_response"); assertEquals(expectedErrors, auth2.getErrors()); assertThat(auth2.getLastErrorReason(), containsString("Invalid issuer in the Assertion/Response")); + assertTrue(auth2.getLastValidationException() instanceof ValidationError); samlResponseEncoded = Util.getFileAsString("data/responses/valid_response.xml.base64"); when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded})); @@ -877,6 +880,7 @@ public void testIsAuthenticated() throws Exception { assertTrue(auth3.isAuthenticated()); assertTrue(auth3.getErrors().isEmpty()); assertNull(auth3.getLastErrorReason()); + assertNull(auth3.getLastValidationException()); } /** From 9c5608e917e28ba826ce71a99738e9ee8aa021d3 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 21 Jul 2020 22:57:19 +0000 Subject: [PATCH 008/133] fix: upgrade org.slf4j:slf4j-api from 1.7.12 to 1.7.30 Snyk has created this PR to upgrade org.slf4j:slf4j-api from 1.7.12 to 1.7.30. See this package in Maven Repository: https://mvnrepository.com/artifact/org.slf4j/slf4j-api/ See this project in Snyk: https://app.snyk.io/org/oneloginorg/project/874c0ad4-1895-4a72-a702-fced5a510080?utm_source=github&utm_medium=upgrade-pr --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 238cc20f..8ce1f853 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ UTF-8 - 1.7.12 + 1.7.30 4.12 1.2.3 3.4 From 0b0056b3fb319a4fe819d8b3e5378366fc5aa57b Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 21 Jul 2020 22:57:24 +0000 Subject: [PATCH 009/133] fix: upgrade org.apache.commons:commons-lang3 from 3.4 to 3.10 Snyk has created this PR to upgrade org.apache.commons:commons-lang3 from 3.4 to 3.10. See this package in Maven Repository: https://mvnrepository.com/artifact/org.apache.commons/commons-lang3/ See this project in Snyk: https://app.snyk.io/org/oneloginorg/project/874c0ad4-1895-4a72-a702-fced5a510080?utm_source=github&utm_medium=upgrade-pr --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 238cc20f..4a9b1557 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 1.7.12 4.12 1.2.3 - 3.4 + 3.10 From a4b730bb8b0998fb4f7b7c3e42c4aecf935ceac8 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 21 Jul 2020 22:57:28 +0000 Subject: [PATCH 010/133] fix: upgrade joda-time:joda-time from 2.10.3 to 2.10.6 Snyk has created this PR to upgrade joda-time:joda-time from 2.10.3 to 2.10.6. See this package in Maven Repository: https://mvnrepository.com/artifact/joda-time/joda-time/ See this project in Snyk: https://app.snyk.io/org/oneloginorg/project/874c0ad4-1895-4a72-a702-fced5a510080?utm_source=github&utm_medium=upgrade-pr --- toolkit/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 9159663f..17f87ce2 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -69,7 +69,7 @@ joda-time joda-time - 2.10.3 + 2.10.6 From f115ea227b19b3cc55d0ca2fa8a561b9b3b1b447 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 21 Jul 2020 22:57:33 +0000 Subject: [PATCH 011/133] fix: upgrade org.apache.santuario:xmlsec from 2.1.4 to 2.2.0 Snyk has created this PR to upgrade org.apache.santuario:xmlsec from 2.1.4 to 2.2.0. See this package in Maven Repository: https://mvnrepository.com/artifact/org.apache.santuario/xmlsec/ See this project in Snyk: https://app.snyk.io/org/oneloginorg/project/874c0ad4-1895-4a72-a702-fced5a510080?utm_source=github&utm_medium=upgrade-pr --- toolkit/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 9159663f..a0440494 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -81,7 +81,7 @@ org.apache.santuario xmlsec - 2.1.4 + 2.2.0 commons-codec From 8ce52e7f183cb15f6c0f27f66e1c55fbc01cca8e Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 21 Jul 2020 22:57:37 +0000 Subject: [PATCH 012/133] fix: upgrade commons-codec:commons-codec from 1.12 to 1.14 Snyk has created this PR to upgrade commons-codec:commons-codec from 1.12 to 1.14. See this package in Maven Repository: https://mvnrepository.com/artifact/commons-codec/commons-codec/ See this project in Snyk: https://app.snyk.io/org/oneloginorg/project/874c0ad4-1895-4a72-a702-fced5a510080?utm_source=github&utm_medium=upgrade-pr --- toolkit/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 9159663f..169293c6 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -86,7 +86,7 @@ commons-codec commons-codec - 1.12 + 1.14 From f81b6d76351cbf7d3f3fbd675eaa050c354b2646 Mon Sep 17 00:00:00 2001 From: Corentin Groix Date: Wed, 22 Jul 2020 20:58:14 +0200 Subject: [PATCH 013/133] Fix some time sensitive tests Some test were failing since last June, because some notOnOrAfter saml node expired. Since the library use Joda Time for these check, we can use the DateTimeUtils.setCurrentMillisFixed utility to make them pass. --- .../saml2/test/authn/AuthnResponseTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 739e3861..2be6faa0 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 @@ -12,7 +12,12 @@ 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; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -23,6 +28,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -50,6 +56,18 @@ public class AuthnResponseTest { @Rule public ExpectedException expectedEx = ExpectedException.none(); + @Before + public void setDateTime() { + //All calls to Joda time check will use this timestamp as "now" value : + setDateTime("2020-06-01T00:00:00Z"); + } + + @After + public void goBackToNormal() { + DateTimeUtils.setCurrentMillisSystem(); + } + + /** * Tests the constructor of SamlResponse * @@ -2678,5 +2696,10 @@ private static HttpRequest newHttpRequest(String samlResponseEncoded) { private static HttpRequest newHttpRequest(String requestURL, String samlResponseEncoded) { return new HttpRequest(requestURL).addParameter("SAMLResponse", samlResponseEncoded); } + + private void setDateTime(String ISOTimeStamp) { + DateTime dateTime = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC().parseDateTime(ISOTimeStamp); + DateTimeUtils.setCurrentMillisFixed(dateTime.toDate().getTime()); + } } From f5a9c21fef9c998a7c04cb89bc681d62f95a1ab4 Mon Sep 17 00:00:00 2001 From: Corentin Groix Date: Wed, 22 Jul 2020 21:07:48 +0200 Subject: [PATCH 014/133] Add precise timestamp support Azure Active Directory B2C issue saml response with sub millisecond timestamps, which could not be parsed. This commit change the parser using pitbulk tip https://github.com/onelogin/java-saml/issues/263 and a test using an almost real life response. --- .../java/com/onelogin/saml2/util/Util.java | 8 +++--- .../saml2/test/authn/AuthnResponseTest.java | 27 +++++++++++++++++++ .../invalids/redacted_azure_b2c.xml.base64 | 1 + 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 core/src/test/resources/data/responses/invalids/redacted_azure_b2c.xml.base64 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 148a2752..a50d140f 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -67,8 +67,8 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.Period; -import org.joda.time.format.DateTimeFormat; 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; @@ -95,9 +95,9 @@ public final class Util { * Private property to construct a logger for this class. */ private static final Logger LOGGER = LoggerFactory.getLogger(Util.class); - - private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").withZone(DateTimeZone.UTC); - private static final DateTimeFormatter DATE_TIME_FORMAT_MILLS = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(DateTimeZone.UTC); + + private static final DateTimeFormatter DATE_TIME_FORMAT = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC(); + private static final DateTimeFormatter DATE_TIME_FORMAT_MILLS = ISODateTimeFormat.dateTime().withZoneUTC(); 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"; 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 2be6faa0..c3a254f9 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 @@ -1866,6 +1866,33 @@ public void testDatetimeWithMiliseconds() throws IOException, Error, XPathExpres assertEquals("No Signature found. SAML Response rejected", samlResponse.getError()); } + @Test + public void testParseAzureB2CTimestamp() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/redacted_azure_b2c.xml.base64"); + + settings.setStrict(false); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse.isValid()); + assertEquals("No Signature found. SAML Response rejected", samlResponse.getError()); + + settings.setStrict(true); + setDateTime("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"); + 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"); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse.isValid()); + assertEquals("Could not validate timestamp: expired. Check system clock.", samlResponse.getError()); + } + /** * Tests the isValid method of SamlResponse * Case: invalid requestId diff --git a/core/src/test/resources/data/responses/invalids/redacted_azure_b2c.xml.base64 b/core/src/test/resources/data/responses/invalids/redacted_azure_b2c.xml.base64 new file mode 100644 index 00000000..17117807 --- /dev/null +++ b/core/src/test/resources/data/responses/invalids/redacted_azure_b2c.xml.base64 @@ -0,0 +1 @@ +PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIElEPSJfNGQyMmRiOTItNzkzMS00OWU4LTkyMGQtNWUwMmYzMDQyNWEzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzM0NTU0OGMwLWFhYmItNGY0Mi04ZDNjLThiY2YzNDRiYjE3NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDctMTZUMDc6NTQ6NTAuMTA3NjkxMVoiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvamF2YS1zYW1sLWpzcHNhbXBsZS9hY3MuanNwIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDpJc3N1ZXIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vPC9zYW1sOklzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiIC8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIElEPSJfYjU2M2FmMTQtNTFjMC00YTA3LTlhYTYtNDk1MDkxNjBmODAwIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMC0wNy0xNlQwNzo1NDo1MC4wNzY0NDAzWiI+PHNhbWw6SXNzdWVyIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+PHNhbWw6U3ViamVjdD48c2FtbDpOYW1lSUQ+NDhkNGQ3YjgtODU5MC00ZjQ2LWJmN2MtNzYxNTIzMjdkZjE2PC9zYW1sOk5hbWVJRD48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDIwLTA3LTE2VDA3OjU5OjUwLjA3NjQ0MDNaIiBSZWNpcGllbnQ9Imh0dHBzOi8vWlpaLWZvb2Jhci5YWFguY29tLy9Mb2dpbi5odG1sIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzM0NTU0OGMwLWFhYmItNGY0Mi04ZDNjLThiY2YzNDRiYjE3NyIgLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAyMC0wNy0xNlQwNzo1NDo1MC4wNzY0NDAzWiIgTm90T25PckFmdGVyPSIyMDIwLTA3LTE2VDA3OjU5OjUwLjA3NjQ0MDNaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9qYXZhLXNhbWwtanNwc2FtcGxlL21ldGFkYXRhLmpzcDwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBTZXNzaW9uSW5kZXg9ImM1MzczMDFmLTUxMmMtNDk3MS05NTIwLWFiNTNhNDA0MzA5NiIgQXV0aG5JbnN0YW50PSIyMDIwLTA3LTE2VDA3OjU0OjUwLjA3NjQ0MDNaIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6dW5zcGVjaWZpZWQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50IHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2dpdmVubmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1cmkiIEZyaWVuZGx5TmFtZT0iRmlyc3QgTmFtZSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+WVlZPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3N1cm5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIiBGcmllbmRseU5hbWU9Ikxhc3QgTmFtZSI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+WFhYPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PHNhbWw6QXR0cmlidXRlIE5hbWU9Imdyb3VwIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIiBGcmllbmRseU5hbWU9IlhYIEFjY2VzcyBHcm91cHMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czphbnlUeXBlIj5kZWFsZXI8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iRW1haWxhZGRyZXNzIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIiBGcmllbmRseU5hbWU9IlByaW1hcnkgRW1haWwgQWRkcmVzcyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+WVlZLlhYWEBtYWlsaW5hdG9yLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2lkZW50aXR5L2NsYWltcy9vYmplY3RpZGVudGlmaWVyIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVyaSIgRnJpZW5kbHlOYW1lPSJVc2VyJ3MgT2JqZWN0IElEIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj40OGQ0ZDdiOC04NTkwLTRmNDYtYmY3Yy03NjE1MjMyN2RmMTY8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhbWw6QXR0cmlidXRlU3RhdGVtZW50Pjwvc2FtbDpBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4= \ No newline at end of file From cb404729ed4bd0ba293bb0ba2622a38906938037 Mon Sep 17 00:00:00 2001 From: chenrui Date: Tue, 1 Sep 2020 16:07:38 -0400 Subject: [PATCH 015/133] Update Util.java --- core/src/main/java/com/onelogin/saml2/util/Util.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8dc3dfe8..5b420418 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -597,9 +597,9 @@ public static String calculateX509Fingerprint(X509Certificate x509cert, String a byte[] dataBytes = x509cert.getEncoded(); if (alg == null || alg.isEmpty() || alg.equals("SHA-1")|| alg.equals("sha1")) { fingerprint = DigestUtils.sha1Hex(dataBytes); - } else if (alg.equals("SHA-256") || alg .equals("sha256")) { + } else if (alg.equals("SHA-256") || alg.equals("sha256")) { fingerprint = DigestUtils.sha256Hex(dataBytes); - } else if (alg.equals("SHA-384") || alg .equals("sha384")) { + } else if (alg.equals("SHA-384") || alg.equals("sha384")) { fingerprint = DigestUtils.sha384Hex(dataBytes); } else if (alg.equals("SHA-512") || alg.equals("sha512")) { fingerprint = DigestUtils.sha512Hex(dataBytes); From 153c1bfa8bef9e1f1a322ac7594c126725e410f3 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 2 Sep 2020 11:48:33 -0400 Subject: [PATCH 016/133] chore(deps): refresh deps to the latest --- core/pom.xml | 6 +++--- pom.xml | 2 +- toolkit/pom.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b2b76aeb..3b209840 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,7 +48,7 @@ joda-time joda-time - 2.10.3 + 2.10.6 @@ -60,12 +60,12 @@ org.apache.santuario xmlsec - 2.1.4 + 2.2.0 commons-codec commons-codec - 1.12 + 1.14 diff --git a/pom.xml b/pom.xml index 3437280a..9b0d2d9e 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 1.7.30 4.12 1.2.3 - 3.10 + 3.11 diff --git a/toolkit/pom.xml b/toolkit/pom.xml index e2c0de6c..edc39381 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -61,7 +61,7 @@ javax.servlet servlet-api - 2.5 + 3.1.0 provided From e33d17bf06948605ddd1d1fc046971958dd95e84 Mon Sep 17 00:00:00 2001 From: Jonathan van Zuijlekom Date: Tue, 8 Sep 2020 16:12:36 +0200 Subject: [PATCH 017/133] Make ProtocolBinding in the AuthnRequest configurable --- core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index 5aafb770..ba3ea21f 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -224,6 +224,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { valueMap.put("issueInstant", issueInstantString); valueMap.put("id", String.valueOf(id)); valueMap.put("assertionConsumerServiceURL", String.valueOf(settings.getSpAssertionConsumerServiceUrl())); + valueMap.put("protocolBinding", settings.getSpAssertionConsumerServiceBinding()); valueMap.put("spEntityid", settings.getSpEntityId()); String requestedAuthnContextStr = ""; @@ -247,7 +248,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { */ private static StringBuilder getAuthnRequestTemplate() { StringBuilder template = new StringBuilder(); - template.append(""); + template.append(""); template.append("${spEntityid}"); template.append("${subjectStr}${nameIDPolicyStr}${requestedAuthnContextStr}"); return template; From 0a6552a879d91a93a2be71ca9748fad982626a4a Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 9 Sep 2020 05:39:32 +0000 Subject: [PATCH 018/133] fix: core/pom.xml to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JAVA-COMMONSCODEC-561518 --- core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b2b76aeb..42cc80c8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -60,7 +60,7 @@ org.apache.santuario xmlsec - 2.1.4 + 2.1.5 commons-codec @@ -117,7 +117,7 @@ - javax.crypto.Cipher.getMaxAllowedKeyLength("AES") > 128 + javax.crypto.Cipher.getMaxAllowedKeyLength("AES") > 128 From b2d3a43307af25a454c270e8ffae2c9a79e4ee2b Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 23 Sep 2020 19:02:48 -0400 Subject: [PATCH 019/133] fix nameidNameQualifier typo closes #282 --- samples/java-saml-tookit-jspsample/src/main/webapp/dologout.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/dologout.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/dologout.jsp index fc640bcd..dc1b9807 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/dologout.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/dologout.jsp @@ -19,7 +19,7 @@ } String nameidNameQualifier = null; if (session.getAttribute("nameidNameQualifier") != null) { - nameIdFormat = session.getAttribute("nameidNameQualifier").toString(); + nameidNameQualifier = session.getAttribute("nameidNameQualifier").toString(); } String nameidSPNameQualifier = null; if (session.getAttribute("nameidSPNameQualifier") != null) { From 5ec744b19d5061c342cfec411855b2063f0ea86d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 25 Sep 2020 12:09:32 +0200 Subject: [PATCH 020/133] Update pom.xml --- core/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 89213c63..0aa0005a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -61,7 +61,6 @@ org.apache.santuario xmlsec 2.2.0 - 2.1.5 commons-codec From 4355b3094ed5440caebe701f4e35993df3eba832 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Sat, 26 Sep 2020 10:52:00 -0400 Subject: [PATCH 021/133] update signature2 in logoutReq/Resp tests Signed-off-by: Rui Chen --- .../saml2/test/logout/LogoutRequestTest.java | 116 +++++++-------- .../saml2/test/logout/LogoutResponseTest.java | 38 ++--- .../test/settings/SettingBuilderTest.java | 140 +++++++++--------- 3 files changed, 147 insertions(+), 147 deletions(-) diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index 78f4eac0..c73c85f0 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -39,12 +39,12 @@ public class LogoutRequestTest { @Rule public ExpectedException expectedEx = ExpectedException.none(); - + /** * Tests the constructor and the getEncodedLogoutRequest method of LogoutRequest * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest#getEncodedLogoutRequest */ @Test @@ -74,7 +74,7 @@ public String getLogoutRequestXml() { logoutRequestStringBase64Deflated = logoutRequest.getEncodedLogoutRequest(false); assertNotEquals(logoutRequestStringBase64Deflated, expectedLogoutRequestStringBase64Deflated); assertEquals(logoutRequestStringBase64Deflated,expectedLogoutRequestStringBase64); - + settings.setCompressRequest(true); logoutRequest = new LogoutRequest(settings) { @Override @@ -102,14 +102,14 @@ public String getLogoutRequestXml() { * Case: Only settings * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest */ @Test public void testConstructor() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); - LogoutRequest logoutRequest = new LogoutRequest(settings); + LogoutRequest logoutRequest = new LogoutRequest(settings); String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); @@ -124,7 +124,7 @@ public void testConstructor() throws Exception { * Case: settings + request * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest */ @Test @@ -145,14 +145,14 @@ public void testConstructorWithRequest() throws Exception { * Case: session index * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest */ @Test public void testConstructorWithSessionIndex() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); String sessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf"; - LogoutRequest logoutRequest = new LogoutRequest(settings, null, null, sessionIndex); + LogoutRequest logoutRequest = new LogoutRequest(settings, null, null, sessionIndex); String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); String expectedSessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf"; @@ -171,7 +171,7 @@ public void testConstructorWithSessionIndex() throws Exception { * Case: encrypted NameId * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest */ @Test @@ -193,7 +193,7 @@ public void testConstructorWithEncryptedNameID() throws Exception { * Tests the getLogoutRequestXml method of LogoutRequest * * @throws Exception - * + * * @see com.onelogin.saml2.logout.LogoutRequest#getLogoutRequestXml */ @Test @@ -209,7 +209,7 @@ public void testGetLogoutRequestXml() throws Exception { logoutRequest = new LogoutRequest(settings, httpRequest); logoutRequestXML = logoutRequest.getLogoutRequestXml(); assertThat(logoutRequestXML, containsString("http://idp.example.com/", ""); samlResponseEncoded = Util.deflatedBase64encoded(logoutRequestStr); httpRequest = newHttpRequest(requestURL, samlResponseEncoded); logoutResponse = new LogoutResponse(settings, httpRequest); - assertNull(logoutResponse.getIssuer()); + assertNull(logoutResponse.getIssuer()); } /** @@ -253,7 +253,7 @@ public void testGetIssuer() throws IOException, URISyntaxException, XMLEntityExc * @throws XMLEntityException * @throws IOException * @throws Error - * + * * @see com.onelogin.saml2.logout.LogoutResponse#isValid */ @Test @@ -269,7 +269,7 @@ public void testIsValidNoResponse() throws XMLEntityException, IOException, Erro httpRequest = new HttpRequest(requestURL, (String)null); logoutResponse = new LogoutResponse(settings, httpRequest); assertFalse(logoutResponse.isValid()); - assertEquals("SAML Logout Response is not loaded", logoutResponse.getError()); + assertEquals("SAML Logout Response is not loaded", logoutResponse.getError()); } /** @@ -289,13 +289,13 @@ public void testIsInValidRequestId() throws XMLEntityException, IOException, URI String samlResponseEncoded = Util.getFileAsString("data/logout_responses/logout_response_deflated.xml.base64"); final String requestURL = "http://stuff.com/endpoints/endpoints/sls.php"; HttpRequest httpRequest = newHttpRequest(requestURL, samlResponseEncoded); - + settings.setStrict(false); LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); assertTrue(logoutResponse.isValid("invalid_request_id")); - + settings.setStrict(true); logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); @@ -321,7 +321,7 @@ public void testIsInValidIssuer() throws XMLEntityException, IOException, URISyn String samlResponseEncoded = Util.getFileAsString("data/logout_responses/invalids/invalid_issuer.xml.base64"); final String requestURL = "http://stuff.com/endpoints/endpoints/sls.php"; HttpRequest httpRequest = newHttpRequest(requestURL, samlResponseEncoded); - + settings.setStrict(false); LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); @@ -362,7 +362,7 @@ public void testIsInValidWrongXML() throws XMLEntityException, IOException, URIS settings.setWantXMLValidation(false); logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); - + settings.setStrict(false); logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); @@ -514,7 +514,7 @@ public void testIsInValidSign() throws URISyntaxException, IOException, XMLEntit .addParameter("RelayState", relayState) .addParameter("SigAlg", sigAlg) .addParameter("Signature", signature); - + LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); assertTrue(logoutResponse.isValid()); @@ -523,7 +523,7 @@ public void testIsInValidSign() throws URISyntaxException, IOException, XMLEntit assertTrue(logoutResponse.isValid()); settings.setStrict(false); - String signature2 = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333="; + String signature2 = "x3Yq1dQ0S/6iirAPpkEYrDvY5mTqzQ3b1eE+sEmnmYbzDs5YHksRrc7uloHt7xqBcCGlk+ZI2USjKshf//OVRkSr8gZ8qYtth1v69hVpEvUdzhSANyJCOCENN2DhX8kc76Wg+VyR1mzbvbrap0G6lrj9TSuM4wyh68gzJDeTQbs="; httpRequest = httpRequest.removeParameter("Signature") .addParameter("Signature", signature2); logoutResponse = new LogoutResponse(settings, httpRequest); @@ -559,7 +559,7 @@ public void testIsInValidSign() throws URISyntaxException, IOException, XMLEntit * @throws IOException * @throws XMLEntityException * @throws Error - * + * * @see com.onelogin.saml2.logout.LogoutResponse#isValid */ @Test diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java index 22019979..1fcf5a7c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java @@ -53,20 +53,20 @@ public class SettingBuilderTest { * Tests SettingsBuilder fromFile method * Case: config file not found * - * @throws IOException + * @throws IOException * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile */ @Test - public void testLoadFromFileNotExist() throws IOException, SettingsException, Error { + public void testLoadFromFileNotExist() throws IOException, Error { expectedEx.expect(Error.class); expectedEx.expectMessage("properties file 'config/config.notfound.properties' not found in the classpath"); - + new SettingsBuilder().fromFile("config/config.notfound.properties").build(); } - + /** * Returns KeyStore details from src/test/resources for testing * @@ -92,32 +92,32 @@ private KeyStoreSettings getKeyStoreSettings() throws KeyStoreException, NoSuchA * Tests SettingsBuilder fromFile method * Case: Config file with KeyStore * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException * @throws SettingsException * @throws Error - * @throws KeyStoreException - * @throws NoSuchAlgorithmException + * @throws KeyStoreException + * @throws NoSuchAlgorithmException * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile */ @Test public void testLoadFromFileAndKeyStore() throws IOException, CertificateException, URISyntaxException, SettingsException, Error, KeyStoreException, NoSuchAlgorithmException { Saml2Settings setting = new SettingsBuilder().fromFile("config/config.empty.properties", getKeyStoreSettings()).build(); - + assertNotNull(setting.getSPcert() instanceof X509Certificate); assertNotNull(setting.getSPkey() instanceof Key); } - + /** * Tests SettingsBuilder fromFile method * Case: empty config file * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException * @throws SettingsException * @throws Error * @@ -169,11 +169,11 @@ public void testLoadFromFileEmpty() throws IOException, CertificateException, UR * Tests SettingsBuilder fromFile method * Case: minimum settings config file * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException - * @throws SettingsException - * @throws Error + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException + * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile */ @@ -226,9 +226,9 @@ public void testLoadFromFileMinProp() throws IOException, CertificateException, * Tests SettingsBuilder fromFile method * Case: all settings config file * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException * @throws SettingsException * @throws Error * @@ -297,10 +297,10 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, * Tests SettingsBuilder fromFile method * Case: settings config file with certificate string * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException - * @throws SettingsException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -343,7 +343,7 @@ public void testLoadFromFileCertString() throws IOException, CertificateExceptio Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); assertTrue(org.equalsTo(setting.getOrganization())); - + assertTrue(setting.getContacts().isEmpty()); } @@ -351,10 +351,10 @@ public void testLoadFromFileCertString() throws IOException, CertificateExceptio * Tests SettingsBuilder fromFile method * Case: settings config file with invalid contact info (not all required fields) * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException - * @throws SettingsException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -409,15 +409,15 @@ public void testLoadFromFileContactString() throws IOException, CertificateExcep assertEquals("Support Guy", c2.getGivenName()); assertTrue(c2.getEmailAddress().isEmpty()); } - + /** * Tests SettingsBuilder fromFile method * Case: settings config file with invalids SP cert/private key * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException - * @throws SettingsException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -425,7 +425,7 @@ public void testLoadFromFileContactString() throws IOException, CertificateExcep @Test public void testLoadFromFileInvalidSPCerts() throws IOException, CertificateException, URISyntaxException, SettingsException, Error { Saml2Settings setting = new SettingsBuilder().fromFile("config/config.invalidspcertstring.properties").build(); - + assertNull(setting.getSPkey()); assertNull(setting.getSPcert()); } @@ -434,10 +434,10 @@ public void testLoadFromFileInvalidSPCerts() throws IOException, CertificateExce * Tests SettingsBuilder fromFile method * Case: Compress * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException - * @throws SettingsException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -457,14 +457,14 @@ public void testCompression() throws IOException, CertificateException, URISynta assertFalse(setting.isCompressRequestEnabled()); assertFalse(setting.isCompressResponseEnabled()); } - + /** * Tests SettingsBuilder fromFile method * Case: settings config file with some empty values * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -515,9 +515,9 @@ public void testLoadFromFileSomeEmptyProp() throws IOException, CertificateExcep * Tests SettingsBuilder fromFile method * Case: settings config file with different values * - * @throws IOException - * @throws CertificateException - * @throws URISyntaxException + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile @@ -608,7 +608,7 @@ public void testFromProperties() throws IOException, Error, CertificateException Base64 encoder = new Base64(64); String x509cert = new String(encoder.encode(setting.getIdpx509cert().getEncoded())); - + Properties prop = new Properties(); prop.setProperty(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY, setting.getIdpEntityId()); prop.setProperty(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, setting.getIdpSingleSignOnServiceUrl().toString()); @@ -617,7 +617,7 @@ public void testFromProperties() throws IOException, Error, CertificateException prop.setProperty(SettingsBuilder.SP_ENTITYID_PROPERTY_KEY, setting.getSpEntityId()); prop.setProperty(SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, setting.getSpAssertionConsumerServiceUrl().toString()); prop.setProperty(SettingsBuilder.SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, setting.getSpSingleLogoutServiceUrl().toString()); - + Saml2Settings setting2 = new SettingsBuilder().fromProperties(prop).build(); assertFalse(setting2.isDebugActive()); @@ -660,7 +660,7 @@ public void testFromProperties() throws IOException, Error, CertificateException assertEquals("ONELOGIN_", setting2.getUniqueIDPrefix()); } - + /** * Tests SettingsBuilder constructor * Case: settings from values @@ -672,9 +672,9 @@ public void testFromProperties() throws IOException, Error, CertificateException @Test public void testLoadFromValues() throws Exception { Map samlData = new LinkedHashMap<>(); - + samlData.put(STRICT_PROPERTY_KEY, "true"); - + // Build SP samlData.put(SP_ENTITYID_PROPERTY_KEY, "http://localhost:8080/java-saml-jspsample/metadata.jsp"); samlData.put(SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, "http://localhost:8080/java-saml-jspsample/acs.jsp"); @@ -685,7 +685,7 @@ public void testLoadFromValues() throws Exception { samlData.put(SP_X509CERT_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_X509CERTNEW_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_PRIVATEKEY_PROPERTY_KEY, "-----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY-----"); - + // Build IdP samlData.put(IDP_ENTITYID_PROPERTY_KEY, "http://idp.example.com/"); samlData.put(IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, "http://idp.example.com/simplesaml/saml2/idp/SSOService.php"); @@ -698,7 +698,7 @@ public void testLoadFromValues() throws Exception { samlData.put(IDP_X509CERTMULTI_PROPERTY_KEY + ".1", "-----BEGIN CERTIFICATE-----\nMIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIyNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAfBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kYvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m\n-----END CERTIFICATE-----"); samlData.put(CERTFINGERPRINT_PROPERTY_KEY, "4b6f70bb2cab82c86a8270f71a880b62e25bc2b3"); samlData.put(CERTFINGERPRINT_ALGORITHM_PROPERTY_KEY, "sha1"); - + // Security samlData.put(SECURITY_NAMEID_ENCRYPTED, "true"); samlData.put(SECURITY_AUTHREQUEST_SIGNED, "true"); @@ -714,7 +714,7 @@ public void testLoadFromValues() throws Exception { samlData.put(SECURITY_REQUESTED_AUTHNCONTEXTCOMPARISON, "exact"); samlData.put(SECURITY_WANT_XML_VALIDATION, "true"); samlData.put(SECURITY_SIGNATURE_ALGORITHM, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); - + // Compress samlData.put(COMPRESS_REQUEST, "false"); samlData.put(COMPRESS_RESPONSE, "false"); @@ -724,7 +724,7 @@ public void testLoadFromValues() throws Exception { samlData.put(ORGANIZATION_DISPLAYNAME, "SP Java Example"); samlData.put(ORGANIZATION_URL, "http://sp.example.com"); samlData.put(ORGANIZATION_LANG, "en"); - + // Contacts samlData.put(CONTACT_TECHNICAL_GIVEN_NAME, "Technical Guy"); samlData.put(CONTACT_TECHNICAL_EMAIL_ADDRESS, "technical@example.org"); @@ -732,9 +732,9 @@ public void testLoadFromValues() throws Exception { samlData.put(CONTACT_SUPPORT_EMAIL_ADDRESS, "support@example.org"); samlData.put(UNIQUE_ID_PREFIX_PROPERTY_KEY, "_"); - + Saml2Settings setting = new SettingsBuilder().fromValues(samlData).build(); - + assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); @@ -815,9 +815,9 @@ public void testLoadFromValues() throws Exception { assertNotNull(newKey); assertFalse(previousCert.equals(newCert)); assertFalse(previousKey.equals(newKey)); - + } - + /** * Tests SettingsBuilder constructor * Case: settings from values @@ -829,9 +829,9 @@ public void testLoadFromValues() throws Exception { @Test public void testLoadFromValuesWithObjects() throws Exception { Map samlData = new LinkedHashMap<>(); - + samlData.put(STRICT_PROPERTY_KEY, true); - + // Build SP samlData.put(SP_ENTITYID_PROPERTY_KEY, "http://localhost:8080/java-saml-jspsample/metadata.jsp"); samlData.put(SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, new URL("http://localhost:8080/java-saml-jspsample/acs.jsp")); @@ -842,7 +842,7 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(SP_X509CERT_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_X509CERTNEW_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_PRIVATEKEY_PROPERTY_KEY, "-----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY-----"); - + // Build IdP samlData.put(IDP_ENTITYID_PROPERTY_KEY, "http://idp.example.com/"); samlData.put(IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, new URL("http://idp.example.com/simplesaml/saml2/idp/SSOService.php")); @@ -853,7 +853,7 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(IDP_X509CERT_PROPERTY_KEY, Util.loadCert(Util.getFileAsString("certs/certificate1"))); samlData.put(CERTFINGERPRINT_PROPERTY_KEY, "4b6f70bb2cab82c86a8270f71a880b62e25bc2b3"); samlData.put(CERTFINGERPRINT_ALGORITHM_PROPERTY_KEY, "sha1"); - + // Security samlData.put(SECURITY_NAMEID_ENCRYPTED, true); samlData.put(SECURITY_AUTHREQUEST_SIGNED, true); @@ -869,7 +869,7 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(SECURITY_REQUESTED_AUTHNCONTEXTCOMPARISON, "exact"); samlData.put(SECURITY_WANT_XML_VALIDATION, true); samlData.put(SECURITY_SIGNATURE_ALGORITHM, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); - + // Compress samlData.put(COMPRESS_REQUEST, "false"); samlData.put(COMPRESS_RESPONSE, "false"); @@ -879,15 +879,15 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(ORGANIZATION_DISPLAYNAME, "SP Java Example"); samlData.put(ORGANIZATION_URL, "http://sp.example.com"); samlData.put(ORGANIZATION_LANG, "en"); - + // Contacts samlData.put(CONTACT_TECHNICAL_GIVEN_NAME, "Technical Guy"); samlData.put(CONTACT_TECHNICAL_EMAIL_ADDRESS, "technical@example.org"); samlData.put(CONTACT_SUPPORT_GIVEN_NAME, "Support Guy"); samlData.put(CONTACT_SUPPORT_EMAIL_ADDRESS, "support@example.org"); - + Saml2Settings setting = new SettingsBuilder().fromValues(samlData).build(); - + assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); @@ -947,7 +947,7 @@ public void testLoadFromValuesWithObjects() throws Exception { assertEquals("ONELOGIN_", setting.getUniqueIDPrefix()); } - + /** * Tests SettingsBuilder constructor * Case: settings config file with certificate loaded from file From 2ec5e1229e506c5d77829c4e6dc4e570bfbabad4 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Sat, 26 Sep 2020 11:10:05 -0400 Subject: [PATCH 022/133] Update the artifactId for java-servlet 3.1.0 Signed-off-by: Rui Chen --- toolkit/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/pom.xml b/toolkit/pom.xml index edc39381..23af75cd 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -60,7 +60,7 @@ javax.servlet - servlet-api + javax.servlet-api 3.1.0 provided From aa89b3d62bc66d406ff000c3e740a102e9f68b3e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 29 Sep 2020 12:25:33 +0200 Subject: [PATCH 023/133] Revert change of Signatures. Update commons-codec to 1.15. Update other libraries --- core/pom.xml | 10 +++++----- .../saml2/test/logout/LogoutRequestTest.java | 2 +- .../saml2/test/logout/LogoutResponseTest.java | 2 +- .../test/settings/SettingBuilderTest.java | 1 - pom.xml | 18 +++++++++--------- toolkit/pom.xml | 8 ++++---- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 0aa0005a..6d2b401d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -65,7 +65,7 @@ commons-codec commons-codec - 1.14 + 1.15 @@ -74,7 +74,7 @@ org.jacoco jacoco-maven-plugin - 0.8.2 + 0.8.6 jacoco.agent.argLine @@ -90,7 +90,7 @@ org.apache.maven.plugins maven-jar-plugin - 2.4 + 3.2.0 @@ -102,7 +102,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.12 + 2.22.2 ${jacoco.agent.argLine} @@ -110,7 +110,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 1.1.1 + 1.4.1 enforce diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index c73c85f0..a8e73d76 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -735,7 +735,7 @@ public void testIsInValidSign() throws Exception { assertTrue(logoutRequest.isValid()); settings.setStrict(false); - String signature2 = "x3Yq1dQ0S/6iirAPpkEYrDvY5mTqzQ3b1eE+sEmnmYbzDs5YHksRrc7uloHt7xqBcCGlk+ZI2USjKshf//OVRkSr8gZ8qYtth1v69hVpEvUdzhSANyJCOCENN2DhX8kc76Wg+VyR1mzbvbrap0G6lrj9TSuM4wyh68gzJDeTQbs="; + String signature2 = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333="; httpRequest = httpRequest.removeParameter("Signature") .addParameter("Signature", signature2); diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index a39786b9..f6f14c58 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -523,7 +523,7 @@ public void testIsInValidSign() throws URISyntaxException, IOException, XMLEntit assertTrue(logoutResponse.isValid()); settings.setStrict(false); - String signature2 = "x3Yq1dQ0S/6iirAPpkEYrDvY5mTqzQ3b1eE+sEmnmYbzDs5YHksRrc7uloHt7xqBcCGlk+ZI2USjKshf//OVRkSr8gZ8qYtth1v69hVpEvUdzhSANyJCOCENN2DhX8kc76Wg+VyR1mzbvbrap0G6lrj9TSuM4wyh68gzJDeTQbs="; + String signature2 = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333="; httpRequest = httpRequest.removeParameter("Signature") .addParameter("Signature", signature2); logoutResponse = new LogoutResponse(settings, httpRequest); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java index 1fcf5a7c..1d46d610 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java @@ -54,7 +54,6 @@ public class SettingBuilderTest { * Case: config file not found * * @throws IOException - * @throws SettingsException * @throws Error * * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile diff --git a/pom.xml b/pom.xml index 9b0d2d9e..8278d65f 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ UTF-8 1.7.30 - 4.12 + 4.13 1.2.3 3.11 @@ -42,13 +42,13 @@ org.hamcrest hamcrest-core - 1.3 + 2.2 test org.hamcrest hamcrest-library - 1.3 + 2.2 test @@ -96,7 +96,7 @@ org.owasp dependency-check-maven - 5.2.1 + 6.0.2 7 @@ -116,7 +116,7 @@ maven-compiler-plugin - 3.5.1 + 3.8.1 1.7 1.7 @@ -125,7 +125,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5 + 2.5.3 true false @@ -183,7 +183,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.2.1 attach-sources @@ -196,7 +196,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.2.0 -Xdoclint:none @@ -212,7 +212,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 23af75cd..48d4d57f 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -61,7 +61,7 @@ javax.servlet javax.servlet-api - 3.1.0 + 4.0.1 provided @@ -86,7 +86,7 @@ commons-codec commons-codec - 1.14 + 1.15 @@ -95,7 +95,7 @@ org.jacoco jacoco-maven-plugin - 0.8.2 + 0.8.6 jacoco.agent.argLine @@ -111,7 +111,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.12 + 2.22.2 ${jacoco.agent.argLine} From 6714bdaa613c0b8bca0f23cd65664400c20680b6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 29 Sep 2020 19:43:54 +0200 Subject: [PATCH 024/133] Move allow_duplicated_attribute_name from sp settings to security. Add documentation --- README.md | 9 ++++++--- .../com/onelogin/saml2/authn/SamlResponse.java | 4 ++-- .../onelogin/saml2/settings/Saml2Settings.java | 18 +++++++++--------- .../saml2/settings/SettingsBuilder.java | 10 +++++----- .../config.allowduplicatednames.properties | 4 ++-- .../main/resources/onelogin.saml.properties | 2 ++ 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d26b00e4..51fefe76 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,8 @@ java-saml (com.onelogin:java-saml-toolkit) has the following dependencies: * For CI: * org.jacoco:jacoco-maven-plugin -also the [Java Cryptography Extension (JCE)](https://en.wikipedia.org/wiki/Java_Cryptography_Extension) is required. If you don't have it, download the version of [jce-6](http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html), [jce-7](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) or [jce-8](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html), unzip it, and drop its content at -*${java.home}/jre/lib/security/* +also the [Java Cryptography Extension (JCE)](https://en.wikipedia.org/wiki/Java_Cryptography_Extension) is required. If you don't have it, download the version of [jce-8](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html), unzip it, and drop its content at +*${java.home}/jre/lib/security/*. JDK 9 and later offer the stronger cryptographic algorithms by default. *toolkit:* * com.onelogin:java-saml-core @@ -123,7 +123,7 @@ also the [Java Cryptography Extension (JCE)](https://en.wikipedia.org/wiki/Java_ * org.apache.maven.plugins:maven-enforcer-plugin For more info, open and read the different pom.xml files: -[core/pom.xml](https://github.com/onelogin/java-saml/blob/v2.2.0/core/pom.xml), [toolkit/pom.xml](https://github.com/onelogin/java-saml/blob/v2.2.0/toolkit/pom.xml) +[core/pom.xml](https://github.com/onelogin/java-saml/blob/v2.5.0/core/pom.xml), [toolkit/pom.xml](https://github.com/onelogin/java-saml/blob/v2.5.0/toolkit/pom.xml) ## Working with the github repository code and Eclipse. ### Get the toolkit. @@ -329,6 +329,9 @@ onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac: # Allows the authn comparison parameter to be set, defaults to 'exact' onelogin.saml2.security.requested_authncontextcomparison = exact +# Allows duplicated names in the attribute statement +onelogin.saml2.security.allow_duplicated_attribute_name = false + # Indicates if the SP will validate all received xmls. # (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). onelogin.saml2.security.want_xml_validation = true 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 6332a06a..932b4457 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -549,14 +549,14 @@ public HashMap> getAttributes() throws XPathExpressionExcep for (int i = 0; i < nodes.getLength(); i++) { NamedNodeMap attrName = nodes.item(i).getAttributes(); String attName = attrName.getNamedItem("Name").getNodeValue(); - if (attributes.containsKey(attName) && !settings.isSpAllowRepeatAttributeName()) { + if (attributes.containsKey(attName) && !settings.isAllowRepeatAttributeName()) { throw new ValidationError("Found an Attribute element with duplicated Name", ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND); } NodeList childrens = nodes.item(i).getChildNodes(); List attrValues = null; - if (attributes.containsKey(attName) && settings.isSpAllowRepeatAttributeName()) { + if (attributes.containsKey(attName) && settings.isAllowRepeatAttributeName()) { attrValues = attributes.get(attName); } else { attrValues = new ArrayList(); diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index 94365e4f..e49eb7d4 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -39,7 +39,6 @@ public class Saml2Settings { private URL spSingleLogoutServiceUrl = null; private String spSingleLogoutServiceBinding = Constants.BINDING_HTTP_REDIRECT; private String spNameIDFormat = Constants.NAMEID_UNSPECIFIED; - private boolean spAllowRepeatAttributeName = false; private X509Certificate spX509cert = null; private X509Certificate spX509certNew = null; private PrivateKey spPrivateKey = null; @@ -73,6 +72,7 @@ public class Saml2Settings { private String signatureAlgorithm = Constants.RSA_SHA1; private String digestAlgorithm = Constants.SHA1; private boolean rejectUnsolicitedResponsesWithInResponseTo = false; + private boolean allowRepeatAttributeName = false; private String uniqueIDPrefix = null; // Compress @@ -135,10 +135,10 @@ public final String getSpNameIDFormat() { } /** - * @return the spAllowRepeatAttributeName setting value + * @return the allowRepeatAttributeName setting value */ - public boolean isSpAllowRepeatAttributeName () { - return spAllowRepeatAttributeName; + public boolean isAllowRepeatAttributeName () { + return allowRepeatAttributeName; } /** @@ -450,13 +450,13 @@ protected final void setSpNameIDFormat(String spNameIDFormat) { } /** - * Set the spAllowRepeatAttributeName setting value + * Set the allowRepeatAttributeName setting value * - * @param spAllowRepeatAttributeName - * the spAllowRepeatAttributeName value to be set + * @param allowRepeatAttributeName + * the allowRepeatAttributeName value to be set */ - public void setSpAllowRepeatAttributeName (boolean spAllowRepeatAttributeName) { - this.spAllowRepeatAttributeName = spAllowRepeatAttributeName; + public void setAllowRepeatAttributeName (boolean allowRepeatAttributeName) { + this.allowRepeatAttributeName = allowRepeatAttributeName; } /** diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index ad92036c..9a706e5a 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -60,7 +60,6 @@ public class SettingsBuilder { public final static String SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY = "onelogin.saml2.sp.single_logout_service.url"; public final static String SP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY = "onelogin.saml2.sp.single_logout_service.binding"; public final static String SP_NAMEIDFORMAT_PROPERTY_KEY = "onelogin.saml2.sp.nameidformat"; - public final static String SP_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.sp.allow_duplicated_attribute_name"; public final static String SP_X509CERT_PROPERTY_KEY = "onelogin.saml2.sp.x509cert"; public final static String SP_PRIVATEKEY_PROPERTY_KEY = "onelogin.saml2.sp.privatekey"; @@ -100,6 +99,7 @@ public class SettingsBuilder { public final static String SECURITY_WANT_XML_VALIDATION = "onelogin.saml2.security.want_xml_validation"; public final static String SECURITY_SIGNATURE_ALGORITHM = "onelogin.saml2.security.signature_algorithm"; public final static String SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO = "onelogin.saml2.security.reject_unsolicited_responses_with_inresponseto"; + public final static String SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.security.allow_duplicated_attribute_name"; // Compress public final static String COMPRESS_REQUEST = "onelogin.saml2.compress.request"; @@ -369,6 +369,10 @@ private void loadSecuritySetting() { if (rejectUnsolicitedResponsesWithInResponseTo != null) { saml2Setting.setRejectUnsolicitedResponsesWithInResponseTo(rejectUnsolicitedResponsesWithInResponseTo); } + + Boolean allowRepeatAttributeName = loadBooleanProperty(SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY); + if (allowRepeatAttributeName != null) + saml2Setting.setAllowRepeatAttributeName(allowRepeatAttributeName); } /** @@ -469,10 +473,6 @@ private void loadSpSetting() { if (spNameIDFormat != null && !spNameIDFormat.isEmpty()) saml2Setting.setSpNameIDFormat(spNameIDFormat); - Boolean spAllowRepeatAttributeName = loadBooleanProperty(SP_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY); - if (spAllowRepeatAttributeName != null) - saml2Setting.setSpAllowRepeatAttributeName(spAllowRepeatAttributeName); - boolean keyStoreEnabled = this.samlData.get(KEYSTORE_KEY) != null && this.samlData.get(KEYSTORE_ALIAS) != null && this.samlData.get(KEYSTORE_KEY_PASSWORD) != null; diff --git a/core/src/test/resources/config/config.allowduplicatednames.properties b/core/src/test/resources/config/config.allowduplicatednames.properties index 4cfec295..9c31a66e 100644 --- a/core/src/test/resources/config/config.allowduplicatednames.properties +++ b/core/src/test/resources/config/config.allowduplicatednames.properties @@ -31,8 +31,6 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified -# Enable duplicated names in the attribute statement -onelogin.saml2.sp.allow_duplicated_attribute_name = true # Usually x509cert and privateKey of the SP are provided by files placed at # the certs folder. But we can also provide them with the following parameters @@ -115,6 +113,8 @@ onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac: # Allows the authn comparison parameter to be set, defaults to 'exact' onelogin.saml2.security.requested_authncontextcomparison = exact +# Enable duplicated names in the attribute statement +onelogin.saml2.security.allow_duplicated_attribute_name = true # Indicates if the SP will validate all received xmls. # (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). diff --git a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties index 328e48d5..4d705fa6 100644 --- a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties +++ b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties @@ -132,6 +132,8 @@ onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac: # Allows the authn comparison parameter to be set, defaults to 'exact' onelogin.saml2.security.onelogin.saml2.security.requested_authncontextcomparison = exact +# Allows duplicated names in the attribute statement +onelogin.saml2.security.allow_duplicated_attribute_name = false # Indicates if the SP will validate all received xmls. # (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). From 7abfd422284de266e185149eba54638ad2507404 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 30 Sep 2020 13:10:34 +0200 Subject: [PATCH 025/133] Update demo to validate settings. --- .../src/main/webapp/metadata.jsp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/metadata.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/metadata.jsp index 72cc1af2..1b2388e0 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/metadata.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/metadata.jsp @@ -2,9 +2,10 @@ Auth auth = new Auth(); Saml2Settings settings = auth.getSettings(); settings.setSPValidationOnly(true); -String metadata = settings.getSPMetadata(); -List errors = Saml2Settings.validateMetadata(metadata); +List errors = settings.checkSettings(); + if (errors.isEmpty()) { + String metadata = settings.getSPMetadata(); out.println(metadata); } else { response.setContentType("text/html; charset=UTF-8"); @@ -12,4 +13,4 @@ if (errors.isEmpty()) { for (String error : errors) { out.println("

"+error+"

"); } -}%> \ No newline at end of file +}%> From 4444fa03e63c363b15f4faece02ea41a2d910693 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 30 Sep 2020 14:25:25 +0200 Subject: [PATCH 026/133] Update pom.xml to use Java 8 --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 8278d65f..da9730bd 100644 --- a/pom.xml +++ b/pom.xml @@ -85,8 +85,8 @@ 3.2.5 - - [1.7,) + + [1.8,) @@ -118,8 +118,8 @@ maven-compiler-plugin 3.8.1 - 1.7 - 1.7 + 1.8 + 1.8 From 5bba700460ac1756436a96dfc89c21986aaf48f4 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 30 Sep 2020 16:49:52 +0200 Subject: [PATCH 027/133] Implement basic, top-level exception handling class. Author: necouchman #206 --- .../com/onelogin/saml2/exception/Error.java | 28 ++++++------ .../saml2/exception/SAMLException.java | 44 +++++++++++++++++++ .../saml2/exception/SettingsException.java | 28 ++++++------ .../saml2/exception/ValidationError.java | 14 +++--- .../saml2/exception/XMLEntityException.java | 2 +- .../com/onelogin/saml2/settings/Metadata.java | 22 +++++----- .../onelogin/saml2/test/util/UtilsTest.java | 2 +- 7 files changed, 92 insertions(+), 48 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/exception/SAMLException.java diff --git a/core/src/main/java/com/onelogin/saml2/exception/Error.java b/core/src/main/java/com/onelogin/saml2/exception/Error.java index 8dc0a58d..ef91559b 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/Error.java +++ b/core/src/main/java/com/onelogin/saml2/exception/Error.java @@ -1,26 +1,26 @@ package com.onelogin.saml2.exception; -public class Error extends Exception { +public class Error extends SAMLException { private static final long serialVersionUID = 1L; - public static final int SETTINGS_FILE_NOT_FOUND = 1; - public static final int METADATA_SP_INVALID = 2; - public static final int SAML_RESPONSE_NOT_FOUND = 3; - public static final int SAML_LOGOUTMESSAGE_NOT_FOUND = 4; - public static final int SAML_LOGOUTREQUEST_INVALID = 5; - public static final int SAML_LOGOUTRESPONSE_INVALID = 6; - public static final int SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 7; - + public static final int SETTINGS_FILE_NOT_FOUND = 1; + public static final int METADATA_SP_INVALID = 2; + public static final int SAML_RESPONSE_NOT_FOUND = 3; + public static final int SAML_LOGOUTMESSAGE_NOT_FOUND = 4; + public static final int SAML_LOGOUTREQUEST_INVALID = 5; + public static final int SAML_LOGOUTRESPONSE_INVALID = 6; + public static final int SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 7; + private int errorCode; - + public Error(String message, int errorCode) { super(message); this.errorCode = errorCode; } - - public int getErrorCode() { - return errorCode; - } + + public int getErrorCode() { + return errorCode; + } } diff --git a/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java b/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java new file mode 100644 index 00000000..a1e1547f --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java @@ -0,0 +1,44 @@ +package com.onelogin.saml2.exception; + +/** + * Top-level exception class for the OneLogin SAML client. + */ +public class SAMLException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Construct a SAMLException with the provided error message. + * + * @param message + * The human-readable error message associated with this exception. + */ + public SAMLException(String message) { + super(message); + } + + /** + * Construct a SAMLException with the provided cause for the exception. + * + * @param cause + * The upstream cause of this exception. + */ + public SAMLException(Throwable cause) { + super(cause); + } + + /** + * Construct a SAMLException with the provided human-readable error message + * and upstream cause. + * + * @param message + * The human-readable error message associated with this exception. + * + * @param cause + * The upstream cause associated with this exception. + */ + public SAMLException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java b/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java index 0fa11018..0e784e52 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java +++ b/core/src/main/java/com/onelogin/saml2/exception/SettingsException.java @@ -1,25 +1,25 @@ package com.onelogin.saml2.exception; -public class SettingsException extends Exception { +public class SettingsException extends SAMLException { private static final long serialVersionUID = 1L; - public static final int SETTINGS_INVALID_SYNTAX = 1; - public static final int SETTINGS_INVALID = 2; - public static final int CERT_NOT_FOUND = 3; - public static final int PRIVATE_KEY_NOT_FOUND = 4; - public static final int PUBLIC_CERT_FILE_NOT_FOUND = 5; - public static final int PRIVATE_KEY_FILE_NOT_FOUND = 6; - - private int errorCode; - + public static final int SETTINGS_INVALID_SYNTAX = 1; + public static final int SETTINGS_INVALID = 2; + public static final int CERT_NOT_FOUND = 3; + public static final int PRIVATE_KEY_NOT_FOUND = 4; + public static final int PUBLIC_CERT_FILE_NOT_FOUND = 5; + public static final int PRIVATE_KEY_FILE_NOT_FOUND = 6; + + private int errorCode; + public SettingsException(String message, int errorCode) { super(message); this.errorCode = errorCode; } - - public int getErrorCode() { - return errorCode; - } + + public int getErrorCode() { + return errorCode; + } } diff --git a/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java b/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java index cde79212..57ce5978 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java +++ b/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java @@ -1,6 +1,6 @@ package com.onelogin.saml2.exception; -public class ValidationError extends Exception { +public class ValidationError extends SAMLException { private static final long serialVersionUID = 1L; @@ -53,16 +53,16 @@ public class ValidationError extends Exception { public static final int NOT_SUPPORTED = 46; public static final int KEY_ALGORITHM_ERROR = 47; public static final int MISSING_ENCRYPTED_ELEMENT = 48; - - private int errorCode; - + + private int errorCode; + public ValidationError(String message, int errorCode) { super(message); this.errorCode = errorCode; } - public int getErrorCode() { - return errorCode; - } + public int getErrorCode() { + return errorCode; + } } diff --git a/core/src/main/java/com/onelogin/saml2/exception/XMLEntityException.java b/core/src/main/java/com/onelogin/saml2/exception/XMLEntityException.java index b1feac37..fd088c94 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/XMLEntityException.java +++ b/core/src/main/java/com/onelogin/saml2/exception/XMLEntityException.java @@ -1,6 +1,6 @@ package com.onelogin.saml2.exception; -public class XMLEntityException extends Exception { +public class XMLEntityException extends SAMLException { private static final long serialVersionUID = 1L; diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index a472b272..761b9a1a 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -43,7 +43,7 @@ public class Metadata { private static final int SECONDS_CACHED = 604800; // 1 week /** - * AttributeConsumingService + * AttributeConsumingService */ private AttributeConsumingService attributeConsumingService = null; @@ -64,7 +64,7 @@ public class Metadata { /** * Constructs the Metadata object. - * + * * @param settings * Saml2Settings object. Setting data * @param validUntilTime @@ -83,7 +83,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu } else { this.validUntilTime = validUntilTime; } - + this.attributeConsumingService = attributeConsumingService; if (cacheDuration == null) { @@ -95,13 +95,13 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu StrSubstitutor substitutor = generateSubstitutor(settings); String unsignedMetadataString = substitutor.replace(getMetadataTemplate()); - LOGGER.debug("metadata --> " + unsignedMetadataString); - metadataString = unsignedMetadataString; + LOGGER.debug("metadata --> " + unsignedMetadataString); + metadataString = unsignedMetadataString; } /** * Constructs the Metadata object. - * + * * @param settings * Saml2Settings object. Setting data * @param validUntilTime @@ -114,7 +114,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration) throws CertificateEncodingException { this(settings, validUntilTime, cacheDuration, null); } - + /** * Constructs the Metadata object. * @@ -126,7 +126,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu public Metadata(Saml2Settings settings) throws CertificateEncodingException { this(settings, null, null); } - + /** * Substitutes metadata variables within a string by values. * @@ -138,8 +138,8 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws CertificateEncodingException { Map valueMap = new HashMap(); - Boolean wantsEncrypted = settings.getWantAssertionsEncrypted() || settings.getWantNameIdEncrypted(); - + Boolean wantsEncrypted = settings.getWantAssertionsEncrypted() || settings.getWantNameIdEncrypted(); + valueMap.put("id", Util.generateUniqueID(settings.getUniqueIDPrefix())); valueMap.put("validUntilTime", Util.formatDateTime(validUntilTime.getTimeInMillis())); valueMap.put("cacheDuration", String.valueOf(cacheDuration)); @@ -152,7 +152,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); valueMap.put("strAttributeConsumingService", getAttributeConsumingServiceXml()); - + valueMap.put("strKeyDescriptor", toX509KeyDescriptorsXML(settings.getSPcert(), settings.getSPcertNew(), wantsEncrypted)); valueMap.put("strContacts", toContactsXml(settings.getContacts())); valueMap.put("strOrganization", toOrganizationXml(settings.getOrganization())); 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 aa77b1c1..fb647cdf 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 @@ -1114,7 +1114,7 @@ public void testValidateSign() throws URISyntaxException, IOException, Certifica String signedAssertionStr = Util.getFileAsString("data/responses/signed_assertion_response.xml.base64"); String samlSignedAssertionStr = new String(Util.base64decoder(signedAssertionStr)); Document samlSignedAssertionDocument = Util.loadXML(samlSignedAssertionStr); - + assertTrue(Util.validateSign(samlSignedAssertionDocument, cert, null, null, ASSERTION_SIGNATURE_XPATH)); assertTrue(Util.validateSign(samlSignedAssertionDocument, (X509Certificate) null, fingerprint_sha1, null, ASSERTION_SIGNATURE_XPATH)); assertTrue(Util.validateSign(samlSignedAssertionDocument, (X509Certificate) null, fingerprint_sha1, "SHA-1", ASSERTION_SIGNATURE_XPATH)); From a680fa08eda2da0c256107fe5ed560abbdf305e6 Mon Sep 17 00:00:00 2001 From: Erik Assum Date: Thu, 1 Oct 2020 09:54:44 +0200 Subject: [PATCH 028/133] [Fix #283] Expose a new constructor for SamlResponse The SamlResponse class is basically a function which needs settings, url, and a saml-response. This commit adds a costructor which takes only those parameters instead of wrapping it in a HttpRequest. --- .../onelogin/saml2/authn/SamlResponse.java | 33 +++++++++++++++---- .../saml2/test/authn/AuthnResponseTest.java | 32 ++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) 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 932b4457..4c35dbf7 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -81,6 +81,32 @@ public class SamlResponse { */ private Exception validationException; + /** + * Constructor to have a Response object fully built and ready to validate the saml response. + * + * @param settings + * Saml2Settings object. Setting data + * @param currentUrl + * URL of the current host + current view + * + * @param samlResponse + * A string containting the base64 encoded response from the IdP + * + * + * @throws ValidationError + * @throws SettingsException + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + */ + public SamlResponse(Saml2Settings settings, String currentUrl, String samlResponse) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, SettingsException, ValidationError { + this.settings = settings; + this.currentUrl = currentUrl; + loadXmlFromBase64(samlResponse); + } + /** * Constructor to have a Response object fully built and ready to validate the saml response. * @@ -98,12 +124,7 @@ public class SamlResponse { * */ public SamlResponse(Saml2Settings settings, HttpRequest request) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, SettingsException, ValidationError { - this.settings = settings; - - if (request != null) { - currentUrl = request.getRequestURL(); - loadXmlFromBase64(request.getParameter("SAMLResponse")); - } + this(settings, request.getRequestURL(), request.getParameter("SAMLResponse")); } /** 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 1bbe0ff8..5fb2f6b8 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 @@ -69,9 +69,37 @@ public void goBackToNormal() { DateTimeUtils.setCurrentMillisSystem(); } - /** - * Tests the constructor of SamlResponse + * Tests the deconstructed constructor of SamlResponse + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse + */ + @Test + public void testDeconstructedConstructor() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + + final String requestURL = "/"; + String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64"); + + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(requestURL, samlResponseEncoded)); + assertTrue(samlResponse instanceof SamlResponse); + + samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64"); + samlResponse = new SamlResponse(settings, requestURL, samlResponseEncoded); + assertTrue(samlResponse instanceof SamlResponse); + } + + + /** + * Tests the httpRequest constructor of SamlResponse * * @throws Error * @throws IOException From 1e4534df6fd403fd917efaf78193e006645ebccb Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 5 Oct 2020 18:29:59 +0200 Subject: [PATCH 029/133] Refactor. Add test. Add support in LogoutResponse --- .../onelogin/saml2/authn/SamlResponse.java | 13 +++---- .../onelogin/saml2/logout/LogoutResponse.java | 7 +++- .../saml2/test/authn/AuthnResponseTest.java | 3 +- .../main/java/com/onelogin/saml2/Auth.java | 25 +++++++------ .../com/onelogin/saml2/test/AuthTest.java | 36 +++++++++++++++++-- 5 files changed, 63 insertions(+), 21 deletions(-) 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 3b5e7312..5e931021 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -81,6 +81,11 @@ public class SamlResponse { */ private Exception validationException; + /** + * The respone status code and messages + */ + private SamlResponseStatus responseStatus; + /** * Constructor to have a Response object fully built and ready to validate the saml response. * @@ -107,11 +112,6 @@ public SamlResponse(Saml2Settings settings, String currentUrl, String samlRespon loadXmlFromBase64(samlResponse); } - /** - * The respone status code and messages - */ - private SamlResponseStatus responseStatus; - /** * Constructor to have a Response object fully built and ready to validate the saml response. * @@ -126,6 +126,7 @@ public SamlResponse(Saml2Settings settings, String currentUrl, String samlRespon * @throws SAXException * @throws ParserConfigurationException * @throws XPathExpressionException + * @throws NullPointerException * */ public SamlResponse(Saml2Settings settings, HttpRequest request) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, SettingsException, ValidationError { @@ -603,7 +604,7 @@ public HashMap> getAttributes() throws XPathExpressionExcep } /** - * Returns the latest response status + * Returns the ResponseStatus object * * @return */ 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 b89c522d..6d2b990e 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -84,6 +84,11 @@ public class LogoutResponse { */ private Exception validationException; + /** + * The respone status code and messages + */ + private SamlResponseStatus responseStatus; + /** * Constructs the LogoutResponse object. * @@ -323,7 +328,7 @@ public String getStatus() throws XPathExpressionException */ public SamlResponseStatus getSamlResponseStatus() throws ValidationError { - String statusXpath = "/samlp:Response/samlp:Status"; + String statusXpath = "/samlp:LogoutResponse/samlp:Status"; return Util.getStatus(statusXpath, this.logoutResponseDocument); } 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 5fb2f6b8..f4884062 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 @@ -1345,9 +1345,8 @@ public void testValidateTimestampsNB() throws ValidationError, XPathExpressionEx @Test public void testNullRequest() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + expectedEx.expect(NullPointerException.class); SamlResponse samlResponse = new SamlResponse(settings, null); - assertFalse(samlResponse.isValid()); - assertEquals("SAML Response is not loaded", samlResponse.getError()); } /** diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 1b484077..bb2d48dd 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -754,17 +754,17 @@ public void processResponse(String requestId) throws Exception { validationException = samlResponse.getValidationException(); SamlResponseStatus samlResponseStatus = samlResponse.getResponseStatus(); if (samlResponseStatus.getStatusCode() == null || !samlResponseStatus.getStatusCode().equals(Constants.STATUS_SUCCESS)) { - errors.add("response_not_success"); - LOGGER.error("processResponse error. sso_not_success"); - LOGGER.debug(" --> " + samlResponseParameter); + errors.add("response_not_success"); + LOGGER.error("processResponse error. sso_not_success"); + LOGGER.debug(" --> " + samlResponseParameter); errors.add(samlResponseStatus.getStatusCode()); - if (samlResponseStatus.getSubStatusCode() != null) { - errors.add(samlResponseStatus.getSubStatusCode()); - } + if (samlResponseStatus.getSubStatusCode() != null) { + errors.add(samlResponseStatus.getSubStatusCode()); + } } else { - errors.add("invalid_response"); - LOGGER.error("processResponse error. invalid_response"); - LOGGER.debug(" --> " + samlResponseParameter); + errors.add("invalid_response"); + LOGGER.error("processResponse error. invalid_response"); + LOGGER.debug(" --> " + samlResponseParameter); } } } else { @@ -810,11 +810,16 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except errorReason = logoutResponse.getError(); validationException = logoutResponse.getValidationException(); } else { - String status = logoutResponse.getStatus(); + SamlResponseStatus samlResponseStatus = logoutResponse.getSamlResponseStatus(); + String status = samlResponseStatus.getStatusCode(); if (status == null || !status.equals(Constants.STATUS_SUCCESS)) { errors.add("logout_not_success"); LOGGER.error("processSLO error. logout_not_success"); LOGGER.debug(" --> " + samlResponseParameter); + errors.add(samlResponseStatus.getStatusCode()); + if (samlResponseStatus.getSubStatusCode() != null) { + errors.add(samlResponseStatus.getSubStatusCode()); + } } else { lastMessageId = logoutResponse.getId(); LOGGER.debug("processSLO success --> " + samlResponseParameter); 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 e7ccbeb0..faa40013 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -64,6 +64,7 @@ import com.onelogin.saml2.util.Util; import org.mockito.ArgumentCaptor; +import org.w3c.dom.Document; public class AuthTest { @@ -563,6 +564,38 @@ public void testProcessResponse() throws Exception { assertEquals(keys, auth2.getAttributesName()); } + /** + * Tests the processResponse methods of Auth + * Case: process Response, status code Responder and sub status + * + * @throws Exception + * + * @see com.onelogin.saml2.Auth#processSLO + */ + @Test + public void testProcessResponseStatusResponder() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + when(request.getRequestURL()).thenReturn(new StringBuffer("https://example.com/opensso/Consumer/metaAlias/sp")); + when(request.getSession()).thenReturn(session); + + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/status_code_and_sub_status_code_responder_and_msg.xml.base64"); + Document samlResponseDoc = Util.loadXML(new String(Util.base64decoder(samlResponseEncoded))); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded})); + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Auth auth = new Auth(settings, request, response); + assertFalse(auth.isAuthenticated()); + assertTrue(auth.getErrors().isEmpty()); + auth.processResponse(); + verify(session, times(0)).invalidate(); + assertFalse(auth.getErrors().isEmpty()); + assertEquals("The status code of the Response was not Success, was urn:oasis:names:tc:SAML:2.0:status:Responder -> something_is_wrong", auth.getLastErrorReason()); + assertTrue(auth.getErrors().contains("response_not_success")); + assertTrue(auth.getErrors().contains(Constants.STATUS_RESPONDER)); + assertTrue(auth.getErrors().contains(Constants.STATUS_AUTHNFAILED)); + } + /** * Tests the processSLO methods of Auth * @@ -825,6 +858,7 @@ public void testProcessSLOResponseStatusResponder() throws Exception { verify(session, times(0)).invalidate(); assertFalse(auth.getErrors().isEmpty()); assertTrue(auth.getErrors().contains("logout_not_success")); + assertTrue(auth.getErrors().contains(Constants.STATUS_RESPONDER)); } /** @@ -853,7 +887,6 @@ public void testIsAuthenticated() throws Exception { assertFalse(auth.getErrors().isEmpty()); List expectedErrors = new ArrayList(); expectedErrors.add("invalid_response"); - expectedErrors.add("urn:oasis:names:tc:SAML:2.0:status:Success"); assertEquals(expectedErrors, auth.getErrors()); assertEquals("SAML Response must contain 1 Assertion.", auth.getLastErrorReason()); assertTrue(auth.getLastValidationException() instanceof ValidationError); @@ -868,7 +901,6 @@ public void testIsAuthenticated() throws Exception { assertFalse(auth2.getErrors().isEmpty()); expectedErrors = new ArrayList(); expectedErrors.add("invalid_response"); - expectedErrors.add("urn:oasis:names:tc:SAML:2.0:status:Success"); assertEquals(expectedErrors, auth2.getErrors()); assertThat(auth2.getLastErrorReason(), containsString("Invalid issuer in the Assertion/Response")); assertTrue(auth2.getLastValidationException() instanceof ValidationError); From 79544784997b6dcd05e3da0618beb95a1b975dde Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 18:58:24 +0200 Subject: [PATCH 030/133] #250 Add a stay parameter to Auth processSlo --- .../main/java/com/onelogin/saml2/Auth.java | 31 +++++- .../com/onelogin/saml2/test/AuthTest.java | 101 ++++++++++++++++-- 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index bb2d48dd..7b02c178 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -598,7 +598,6 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * LogoutRequest. * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in * the LogoutRequest. - * * @throws IOException * @throws XMLEntityException * @throws SettingsException @@ -791,10 +790,14 @@ public void processResponse() throws Exception { * destroy it * @param requestId The ID of the LogoutRequest sent by this SP to the * IdP + * @param stay True if we want to stay (returns the url string) False + * to execute redirection + * + * @return the URL with the Logout Message if stay = True * * @throws Exception */ - public void processSLO(Boolean keepLocalSession, String requestId) throws Exception { + public String processSLO(Boolean keepLocalSession, String requestId, Boolean stay) throws Exception { final HttpRequest httpRequest = ServletUtils.makeHttpRequest(this.request); final String samlRequestParameter = httpRequest.getParameter("SAMLRequest"); @@ -828,6 +831,7 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except } } } + return null; } else if (samlRequestParameter != null) { LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); lastRequest = logoutRequest.getLogoutRequestXml(); @@ -837,6 +841,7 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except LOGGER.debug(" --> " + samlRequestParameter); errorReason = logoutRequest.getError(); validationException = logoutRequest.getValidationException(); + return null; } else { lastMessageId = logoutRequest.getId(); LOGGER.debug("processSLO success --> " + samlRequestParameter); @@ -869,8 +874,11 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except } String sloUrl = getSLOResponseUrl(); - LOGGER.debug("Logout response sent to " + sloUrl + " --> " + samlLogoutResponse); - ServletUtils.sendRedirect(response, sloUrl, parameters); + + if (!stay) { + LOGGER.debug("Logout response sent to " + sloUrl + " --> " + samlLogoutResponse); + } + return ServletUtils.sendRedirect(response, sloUrl, parameters, stay); } } else { errors.add("invalid_binding"); @@ -880,6 +888,21 @@ public void processSLO(Boolean keepLocalSession, String requestId) throws Except } } + /** + * Process the SAML Logout Response / Logout Request sent by the IdP. + * + * @param keepLocalSession When true will keep the local session, otherwise will + * destroy it + * @param requestId The ID of the LogoutRequest sent by this SP to the + * IdP + * + * + * @throws Exception + */ + public void processSLO(Boolean keepLocalSession, String requestId) throws Exception { + processSLO(keepLocalSession, requestId, false); + } + /** * Process the SAML Logout Response / Logout Request sent by the IdP. * 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 faa40013..adae1d07 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -10,6 +10,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.matches; @@ -18,26 +19,18 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.ArrayList; -import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,7 +45,6 @@ import org.junit.rules.ExpectedException; import com.onelogin.saml2.Auth; -import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.SettingsException; @@ -642,7 +634,7 @@ public void testProcessSLORequestKeepSession() throws Exception { when(request.getSession()).thenReturn(session); String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request_deflated.xml.base64"); - when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); Auth auth = new Auth(settings, request, response); @@ -671,7 +663,7 @@ public void testProcessSLORequestRemoveSession() throws Exception { when(request.getSession()).thenReturn(session); String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request_deflated.xml.base64"); - when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); Auth auth = new Auth(settings, request, response); assertFalse(auth.isAuthenticated()); @@ -682,6 +674,93 @@ public void testProcessSLORequestRemoveSession() throws Exception { assertTrue(auth.getErrors().isEmpty()); } + /** + * Tests the processSLO methods of Auth + * Case: process LogoutRequest, remove session, no stay + * + * @throws Exception + * + * @see com.onelogin.saml2.Auth#processSLO + */ + @Test + public void testProcessSLORequestStay() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + when(request.getRequestURL()).thenReturn(new StringBuffer("http://stuff.com/endpoints/endpoints/sls.php")); + when(request.getSession()).thenReturn(session); + + String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request_deflated.xml.base64"); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Auth auth = new Auth(settings, request, response); + assertFalse(auth.isAuthenticated()); + assertTrue(auth.getErrors().isEmpty()); + auth.processSLO(false, null); + verify(response).sendRedirect(matches("http:\\/\\/idp.example.com\\/simplesaml\\/saml2\\/idp\\/SingleLogoutService.php\\?SAMLResponse=(.)*")); + verify(session, times(1)).invalidate(); + assertTrue(auth.getErrors().isEmpty()); + } + + /** + * Tests the processSLO methods of Auth + * Case: process LogoutRequest, remove session, stay = false + * + * @throws Exception + * + * @see com.onelogin.saml2.Auth#processSLO + */ + @Test + public void testProcessSLORequestStayFalse() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + when(request.getRequestURL()).thenReturn(new StringBuffer("http://stuff.com/endpoints/endpoints/sls.php")); + when(request.getSession()).thenReturn(session); + + String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request_deflated.xml.base64"); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Auth auth = new Auth(settings, request, response); + assertFalse(auth.isAuthenticated()); + assertTrue(auth.getErrors().isEmpty()); + String target = auth.processSLO(false, null, false); + verify(response).sendRedirect(matches("http:\\/\\/idp.example.com\\/simplesaml\\/saml2\\/idp\\/SingleLogoutService.php\\?SAMLResponse=(.)*")); + verify(response, times(1)).sendRedirect(matches("http:\\/\\/idp.example.com\\/simplesaml\\/saml2\\/idp\\/SingleLogoutService.php\\?SAMLResponse=(.)*")); + verify(session, times(1)).invalidate(); + assertTrue(auth.getErrors().isEmpty()); + assertThat(target, startsWith("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php?SAMLResponse=")); + } + + /** + * Tests the processSLO methods of Auth + * Case: process LogoutRequest, remove session, stay = true + * + * @throws Exception + * + * @see com.onelogin.saml2.Auth#processSLO + */ + @Test + public void testProcessSLORequestStayTrue() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + when(request.getRequestURL()).thenReturn(new StringBuffer("http://stuff.com/endpoints/endpoints/sls.php")); + when(request.getSession()).thenReturn(session); + + String samlRequestEncoded = Util.getFileAsString("data/logout_requests/logout_request_deflated.xml.base64"); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLRequest", new String[]{samlRequestEncoded})); + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Auth auth = new Auth(settings, request, response); + assertFalse(auth.isAuthenticated()); + assertTrue(auth.getErrors().isEmpty()); + String target = auth.processSLO(false, null, true); + verify(response, times(0)).sendRedirect(matches("http:\\/\\/idp.example.com\\/simplesaml\\/saml2\\/idp\\/SingleLogoutService.php\\?SAMLResponse=(.)*")); + verify(session, times(1)).invalidate(); + assertTrue(auth.getErrors().isEmpty()); + assertThat(target, startsWith("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php?SAMLResponse=")); + } + /** * Tests the processSLO methods of Auth * Case: process LogoutRequest, with RelayState and sign response From 2980a46f48ecb6e415eaf8b48d5252e363adc976 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 19:04:59 +0200 Subject: [PATCH 031/133] Prepare Release --- README.md | 2 +- core/pom.xml | 2 +- pom.xml | 2 +- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 51fefe76..c42d496e 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Install it as a maven dependency: com.onelogin java-saml - 2.5.0 + 2.6.0 ``` diff --git a/core/pom.xml b/core/pom.xml index 6d2b401d..3c17f53f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.5.1-SNAPSHOT + 2.6.0-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index da9730bd..7b918601 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.5.1-SNAPSHOT + 2.6.0-SNAPSHOT pom OneLogin java-saml Toolkit Project diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 4ff3ddfa..36aa7e92 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.5.1-SNAPSHOT + 2.6.0-SNAPSHOT java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 8dd4ed5e..cf9a9c7c 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.5.1-SNAPSHOT + 2.6.0-SNAPSHOT java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 48d4d57f..ddf1f2da 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.5.1-SNAPSHOT + 2.6.0-SNAPSHOT jar From b17f18734550eb88cc60de7717fd82acbc74a2a6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 19:34:43 +0200 Subject: [PATCH 032/133] Clean --- core/src/main/java/com/onelogin/saml2/util/Util.java | 2 -- .../com/onelogin/saml2/test/authn/AuthnResponseTest.java | 1 - .../test/java/com/onelogin/saml2/test/util/UtilsTest.java | 1 - pom.xml | 6 +++--- toolkit/src/main/java/com/onelogin/saml2/Auth.java | 1 - toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java | 1 - 6 files changed, 3 insertions(+), 9 deletions(-) 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 20e9b08d..0daa7a69 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -69,9 +69,7 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.exceptions.XMLSecurityException; import org.apache.xml.security.keys.KeyInfo; -import org.apache.xml.security.keys.keyresolver.KeyResolverException; import org.apache.xml.security.signature.XMLSignature; -import org.apache.xml.security.signature.XMLSignatureException; import org.apache.xml.security.transforms.Transforms; import org.apache.xml.security.utils.XMLUtils; import org.joda.time.DateTime; 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 f4884062..bfb2af8e 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 @@ -28,7 +28,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; 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 fb647cdf..61616237 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 @@ -52,7 +52,6 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.SchemaFactory; import com.onelogin.saml2.util.Util; diff --git a/pom.xml b/pom.xml index 7b918601..6e5e9b8c 100644 --- a/pom.xml +++ b/pom.xml @@ -197,12 +197,12 @@ org.apache.maven.plugins maven-javadoc-plugin 3.2.0 - - -Xdoclint:none - attach-javadocs + + none + jar diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 7b02c178..aad28bd8 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.security.InvalidKeyException; -import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; 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 adae1d07..01b372b3 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -10,7 +10,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.matches; From 6377a191d238c17c483f9b81047bf91358f1c17c Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 19:47:48 +0200 Subject: [PATCH 033/133] [maven-release-plugin] prepare release v2.6.0 --- core/pom.xml | 4 ++-- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 3c17f53f..ce23096e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0-SNAPSHOT + 2.6.0 jar @@ -117,7 +117,7 @@ - javax.crypto.Cipher.getMaxAllowedKeyLength("AES") > 128 + javax.crypto.Cipher.getMaxAllowedKeyLength("AES") > 128 diff --git a/pom.xml b/pom.xml index 6e5e9b8c..b360226e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0-SNAPSHOT + 2.6.0 pom OneLogin java-saml Toolkit Project @@ -156,7 +156,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - HEAD + v2.6.0 diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 36aa7e92..2381ce34 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.6.0-SNAPSHOT + 2.6.0 java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index cf9a9c7c..4eb08dec 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0-SNAPSHOT + 2.6.0 java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index ddf1f2da..1beea169 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0-SNAPSHOT + 2.6.0 jar From 4522a0165ac6add071643baa20a033bb5abd46d2 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 19:47:57 +0200 Subject: [PATCH 034/133] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index ce23096e..0303e9a4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0 + 2.6.1-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index b360226e..0fd5370d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0 + 2.6.1-SNAPSHOT pom OneLogin java-saml Toolkit Project @@ -156,7 +156,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - v2.6.0 + HEAD diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 2381ce34..eea4747d 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.6.0 + 2.6.1-SNAPSHOT java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 4eb08dec..7dd43d95 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0 + 2.6.1-SNAPSHOT java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 1beea169..43f79774 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.0 + 2.6.1-SNAPSHOT jar From 0fe03e9b8134fe21236a08deb97e6ea30f92819e Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 9 Oct 2020 21:18:24 +0200 Subject: [PATCH 035/133] Fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c42d496e..e91b0eda 100644 --- a/README.md +++ b/README.md @@ -323,8 +323,8 @@ onelogin.saml2.security.want_nameid_encrypted = false # Authentication context. # Set Empty and no AuthContext will be sent in the AuthNRequest, -# Set comma separated values urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password -onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password +# Set comma separated values urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos,urn:oasis:names:tc:SAML:2.0:ac:classes:Password +onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:Password # Allows the authn comparison parameter to be set, defaults to 'exact' onelogin.saml2.security.requested_authncontextcomparison = exact From 2b8cc9fa30762009ce0428dc9d7212c9018e2c47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 08:37:30 +0000 Subject: [PATCH 036/133] Bump junit from 4.13 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0fd5370d..e9223dc5 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ UTF-8 1.7.30 - 4.13 + 4.13.1 1.2.3 3.11 From 2c0110615c54dec806c2981d9fbbf29be4a4f302 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 13 Oct 2020 16:45:45 +0200 Subject: [PATCH 037/133] Update dependency --- samples/java-saml-tookit-jspsample/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index eea4747d..b9378351 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -19,7 +19,7 @@ javax.servlet javax.servlet-api - 3.1.0 + 4.0.1 provided From ce149dfc2d66f93479e34c20d56a80edac29a870 Mon Sep 17 00:00:00 2001 From: Robert Buttigieg Date: Wed, 21 Oct 2020 17:41:19 +0200 Subject: [PATCH 038/133] 1. Added support for unwrapping the key via an HSM when decrypting the SAML assertion. 2. Added 2 optional dependencies to integrate this library with the Azure Key Vault. 3. Modified the decryptAssertion() method in the SamlResponse class in order to check whether an HSM has been configured or not. If yes, it will call the HSM in order to unwrap the key. 4. Modified the settings validations in the Saml2Settings class in order to validate the following scenarios: a. If the library is configured with both a private key and an HSM, an error (use_either_hsm_or_private_key) is thrown. b. If the library is configured to use an HSM, it does not need to check for the SP certificate. 5. Added a new function decryptUsingHsm() in the Util class and moved the validation on the encrypted data to a new function in order to be used both from the decryptUsingHsm() and decryptElement() functions. 6. Added respective tests for the Saml2Settings class and added also the config.hsm.properties file in order to test the scenario when you have the library configured with both the private key and the HSM. 7. Added a new abstract class HSM, which must be used whenever a new HSM needs to be integrated with this library. 8. Added a new class AzureKeyVault which extends the abstract class and implement its abstract methods. --- .nvd-suppressions.xml | 11 +- core/pom.xml | 14 ++ .../onelogin/saml2/authn/SamlResponse.java | 62 ++++---- .../saml2/model/hsm/AzureKeyVault.java | 139 ++++++++++++++++++ .../com/onelogin/saml2/model/hsm/HSM.java | 44 ++++++ .../saml2/settings/Saml2Settings.java | 37 ++++- .../com/onelogin/saml2/util/Constants.java | 3 + .../java/com/onelogin/saml2/util/Util.java | 103 +++++++++---- .../test/settings/Saml2SettingsTest.java | 90 ++++++++---- .../resources/config/config.hsm.properties | 26 ++++ .../com/onelogin/saml2/test/AuthTest.java | 2 +- 11 files changed, 442 insertions(+), 89 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/model/hsm/AzureKeyVault.java create mode 100644 core/src/main/java/com/onelogin/saml2/model/hsm/HSM.java create mode 100644 core/src/test/resources/config/config.hsm.properties diff --git a/.nvd-suppressions.xml b/.nvd-suppressions.xml index da0ddd85..15890244 100644 --- a/.nvd-suppressions.xml +++ b/.nvd-suppressions.xml @@ -1,4 +1,13 @@ - + + + .*\bmsal4j-1.6.1\.jar + cpe:/a:http_authentication_library_project:http_authentication_library + + + + .*\boauth2-oidc-sdk-6.14\.jar + cpe:/a:openid:openid + diff --git a/core/pom.xml b/core/pom.xml index 0303e9a4..6a57a920 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -67,6 +67,20 @@ commons-codec 1.15 + + + + com.azure + azure-security-keyvault-keys + 4.2.1 + true + + + com.azure + azure-identity + 1.0.9 + true + 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 5e931021..29136337 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -10,6 +10,8 @@ import java.util.Objects; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; + +import com.onelogin.saml2.model.hsm.HSM; import org.joda.time.DateTime; import org.joda.time.Instant; import org.slf4j.Logger; @@ -78,7 +80,7 @@ public class SamlResponse { /** * After validation, if it fails this property has the cause of the problem - */ + */ private Exception validationException; /** @@ -156,7 +158,7 @@ public void loadXmlFromBase64(String responseStr) throws ParserConfigurationExce NodeList encryptedAssertionNodes = samlResponseDocument.getElementsByTagNameNS(Constants.NS_SAML,"EncryptedAssertion"); - if (encryptedAssertionNodes.getLength() != 0) { + if (encryptedAssertionNodes.getLength() != 0) { decryptedDocument = Util.copyDocument(samlResponseDocument); encrypted = true; decryptedDocument = this.decryptAssertion(decryptedDocument); @@ -566,12 +568,12 @@ public String getNameIdSPNameQualifier() throws Exception { * @throws XPathExpressionException * @throws ValidationError * - */ + */ public HashMap> getAttributes() throws XPathExpressionException, ValidationError { HashMap> attributes = new HashMap>(); NodeList nodes = this.queryAssertion("/saml:AttributeStatement/saml:Attribute"); - + if (nodes.getLength() != 0) { for (int i = 0; i < nodes.getLength(); i++) { NamedNodeMap attrName = nodes.item(i).getAttributes(); @@ -579,7 +581,7 @@ public HashMap> getAttributes() throws XPathExpressionExcep if (attributes.containsKey(attName) && !settings.isAllowRepeatAttributeName()) { throw new ValidationError("Found an Attribute element with duplicated Name", ValidationError.DUPLICATED_ATTRIBUTE_NAME_FOUND); } - + NodeList childrens = nodes.item(i).getChildNodes(); List attrValues = null; @@ -605,7 +607,7 @@ public HashMap> getAttributes() throws XPathExpressionExcep /** * Returns the ResponseStatus object - * + * * @return */ public SamlResponseStatus getResponseStatus() { @@ -639,7 +641,7 @@ public void checkStatus() throws ValidationError { * * @throws IllegalArgumentException * if the response not contain status or if Unexpected XPath error - * @throws ValidationError + * @throws ValidationError */ public static SamlResponseStatus getStatus(Document dom) throws ValidationError { String statusXpath = "/samlp:Response/samlp:Status"; @@ -682,7 +684,7 @@ public Boolean checkOneAuthnStatement() throws XPathExpressionException { * Gets the audiences. * * @return the audiences of the response - * + * * @throws XPathExpressionException */ public List getAudiences() throws XPathExpressionException { @@ -706,8 +708,8 @@ public List getAudiences() throws XPathExpressionException { * * @return the issuers of the assertion/response * - * @throws XPathExpressionException - * @throws ValidationError + * @throws XPathExpressionException + * @throws ValidationError */ public List getIssuers() throws XPathExpressionException, ValidationError { List issuers = new ArrayList(); @@ -763,7 +765,7 @@ public DateTime getSessionNotOnOrAfter() throws XPathExpressionException { * * @return the SessionIndex value * - * @throws XPathExpressionException + * @throws XPathExpressionException */ public String getSessionIndex() throws XPathExpressionException { String sessionIndex = null; @@ -852,7 +854,7 @@ public ArrayList processSignedElements() throws XPathExpressionException String responseTag = "{" + Constants.NS_SAMLP + "}Response"; String assertionTag = "{" + Constants.NS_SAML + "}Assertion"; - + if (!signedElement.equals(responseTag) && !signedElement.equals(assertionTag)) { throw new ValidationError("Invalid Signature Element " + signedElement + " SAML Response rejected", ValidationError.WRONG_SIGNED_ELEMENT); } @@ -862,13 +864,13 @@ public ArrayList processSignedElements() throws XPathExpressionException if (idNode == null || idNode.getNodeValue() == null || idNode.getNodeValue().isEmpty()) { throw new ValidationError("Signed Element must contain an ID. SAML Response rejected", ValidationError.ID_NOT_FOUND_IN_SIGNED_ELEMENT); } - - String idValue = idNode.getNodeValue(); + + String idValue = idNode.getNodeValue(); if (verifiedIds.contains(idValue)) { throw new ValidationError("Duplicated ID. SAML Response rejected", ValidationError.DUPLICATED_ID_IN_SIGNED_ELEMENTS); } verifiedIds.add(idValue); - + NodeList refNodes = Util.query(null, "ds:SignedInfo/ds:Reference", signNode); if (refNodes.getLength() == 1) { Node refNode = refNodes.item(0); @@ -878,7 +880,7 @@ public ArrayList processSignedElements() throws XPathExpressionException if (!sei.equals(idValue)) { throw new ValidationError("Found an invalid Signed Element. SAML Response rejected", ValidationError.INVALID_SIGNED_ELEMENT); } - + if (verifiedSeis.contains(sei)) { throw new ValidationError("Duplicated Reference URI. SAML Response rejected", ValidationError.DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS); } @@ -958,7 +960,7 @@ public boolean validateSignedElements(ArrayList signedElements) throws X * * @return true if still valid * - * @throws ValidationError + * @throws ValidationError */ public boolean validateTimestamps() throws ValidationError { NodeList timestampNodes = samlResponseDocument.getElementsByTagNameNS("*", "Conditions"); @@ -1026,7 +1028,7 @@ public Exception getValidationException() { * Xpath Expression * * @return the queried node - * @throws XPathExpressionException + * @throws XPathExpressionException * */ private NodeList queryAssertion(String assertionXpath) throws XPathExpressionException { @@ -1075,7 +1077,7 @@ private NodeList queryAssertion(String assertionXpath) throws XPathExpressionExc * * @param nameQuery * Xpath Expression - * @param context + * @param context * The context node * * @return DOMNodeList The queried nodes @@ -1094,13 +1096,13 @@ private NodeList query(String nameQuery, Node context) throws XPathExpressionExc /** * Decrypt assertion. - * + * * @param dom * Encrypted assertion * * @return Decrypted Assertion. * - * @throws XPathExpressionException + * @throws XPathExpressionException * @throws IOException * @throws SAXException * @throws ParserConfigurationException @@ -1110,7 +1112,9 @@ private NodeList query(String nameQuery, Node context) throws XPathExpressionExc private Document decryptAssertion(Document dom) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, SettingsException, ValidationError { PrivateKey key = settings.getSPkey(); - if (key == null) { + HSM hsm = this.settings.getHsm(); + + if (hsm == null && key == null) { throw new SettingsException("No private key available for decrypt, check settings", SettingsException.PRIVATE_KEY_NOT_FOUND); } @@ -1119,7 +1123,13 @@ private Document decryptAssertion(Document dom) throws XPathExpressionException, throw new ValidationError("No /samlp:Response/saml:EncryptedAssertion/xenc:EncryptedData element found", ValidationError.MISSING_ENCRYPTED_ELEMENT); } Element encryptedData = (Element) encryptedDataNodes.item(0); - Util.decryptElement(encryptedData, key); + + if (hsm != null) { + Util.decryptUsingHsm(encryptedData, hsm); + } else { + Util.decryptElement(encryptedData, key); + } + // We need to Remove the saml:EncryptedAssertion Node NodeList AssertionDataNodes = Util.query(dom, "/samlp:Response/saml:EncryptedAssertion/saml:Assertion"); @@ -1138,7 +1148,7 @@ private Document decryptAssertion(Document dom) throws XPathExpressionException, } /** - * @return the SAMLResponse XML, If the Assertion of the SAMLResponse was encrypted, + * @return the SAMLResponse XML, If the Assertion of the SAMLResponse was encrypted, * returns the XML with the assertion decrypted */ public String getSAMLResponseXml() { @@ -1148,11 +1158,11 @@ public String getSAMLResponseXml() { } else { xml = samlResponseString; } - return xml; + return xml; } /** - * @return the SAMLResponse Document, If the Assertion of the SAMLResponse was encrypted, + * @return the SAMLResponse Document, If the Assertion of the SAMLResponse was encrypted, * returns the Document with the assertion decrypted */ protected Document getSAMLResponseDocument() { diff --git a/core/src/main/java/com/onelogin/saml2/model/hsm/AzureKeyVault.java b/core/src/main/java/com/onelogin/saml2/model/hsm/AzureKeyVault.java new file mode 100644 index 00000000..7abadf0e --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/model/hsm/AzureKeyVault.java @@ -0,0 +1,139 @@ +package com.onelogin.saml2.model.hsm; + +import com.azure.core.http.HttpClient; +import com.azure.core.http.netty.NettyAsyncHttpClientBuilder; +import com.azure.identity.ClientSecretCredential; +import com.azure.identity.ClientSecretCredentialBuilder; +import com.azure.security.keyvault.keys.cryptography.CryptographyClient; +import com.azure.security.keyvault.keys.cryptography.CryptographyClientBuilder; +import com.azure.security.keyvault.keys.cryptography.models.EncryptionAlgorithm; +import com.azure.security.keyvault.keys.cryptography.models.KeyWrapAlgorithm; +import com.onelogin.saml2.util.Constants; + +import java.util.HashMap; + +public class AzureKeyVault extends HSM { + + private String clientId; + private String clientCredentials; + private String tenantId; + private String keyVaultId; + private CryptographyClient akvClient; + private HashMap algorithmMapping; + + /** + * Constructor to initialise an HSM object. + * + * @param clientId The Azure Key Vault client ID. + * @param clientCredentials The Azure Key Vault client credentials. + * @param tenantId The Azure Key Vault tenant ID. + * @param keyVaultId The Azure Key Vault ID. + * @return AzureKeyVault + */ + public AzureKeyVault(String clientId, String clientCredentials, String tenantId, String keyVaultId) { + this.clientId = clientId; + this.clientCredentials = clientCredentials; + this.tenantId = tenantId; + this.keyVaultId = keyVaultId; + + this.algorithmMapping = createAlgorithmMapping(); + } + + /** + * Creates a mapping between the URLs received from the encrypted SAML + * assertion and the algorithms as how they are expected to be received from + * the Azure Key Vault. + * + * @return The algorithm mapping. + */ + private HashMap createAlgorithmMapping() { + HashMap mapping = new HashMap<>(); + + mapping.put(Constants.RSA_1_5, KeyWrapAlgorithm.RSA1_5); + mapping.put(Constants.RSA_OAEP_MGF1P, KeyWrapAlgorithm.RSA_OAEP); + mapping.put(Constants.A128KW, KeyWrapAlgorithm.A128KW); + mapping.put(Constants.A192KW, KeyWrapAlgorithm.A192KW); + mapping.put(Constants.A256KW, KeyWrapAlgorithm.A256KW); + + return mapping; + } + + /** + * Retrieves the key wrap algorithm object based on the algorithm URL passed + * within the SAML assertion. + * + * @param algorithmUrl The algorithm URL. + * @return The KeyWrapAlgorithm. + */ + private KeyWrapAlgorithm getAlgorithm(String algorithmUrl) { + return algorithmMapping.get(algorithmUrl); + } + + /** + * Sets the client to connect to the Azure Key Vault. + */ + @Override + public void setClient() { + ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder() + .clientId(clientId) + .clientSecret(clientCredentials) + .tenantId(tenantId) + .build(); + + HttpClient httpClient = new NettyAsyncHttpClientBuilder().build(); + + this.akvClient = new CryptographyClientBuilder() + .httpClient(httpClient) + .credential(clientSecretCredential) + .keyIdentifier(keyVaultId) + .buildClient(); + } + + /** + * Wraps a key with a particular algorithm using the Azure Key Vault. + * + * @param algorithm The algorithm to use to wrap the key. + * @param key The key to wrap + * @return A wrapped key. + */ + @Override + public byte[] wrapKey(String algorithm, byte[] key) { + return this.akvClient.wrapKey(KeyWrapAlgorithm.fromString(algorithm), key).getEncryptedKey(); + } + + /** + * Unwraps a key with a particular algorithm using the Azure Key Vault. + * + * @param algorithmUrl The algorithm to use to unwrap the key. + * @param wrappedKey The key to unwrap + * @return An unwrapped key. + */ + @Override + public byte[] unwrapKey(String algorithmUrl, byte[] wrappedKey) { + return this.akvClient.unwrapKey(getAlgorithm(algorithmUrl), wrappedKey).getKey(); + } + + /** + * Encrypts an array of bytes with a particular algorithm using the Azure Key Vault. + * + * @param algorithm The algorithm to use for encryption. + * @param plainText The array of bytes to encrypt. + * @return An encrypted array of bytes. + */ + @Override + public byte[] encrypt(String algorithm, byte[] plainText) { + return this.akvClient.encrypt(EncryptionAlgorithm.fromString(algorithm), plainText).getCipherText(); + } + + /** + * Decrypts an array of bytes with a particular algorithm using the Azure Key Vault. + * + * @param algorithm The algorithm to use for decryption. + * @param cipherText The encrypted array of bytes. + * @return A decrypted array of bytes. + */ + @Override + public byte[] decrypt(String algorithm, byte[] cipherText) { + return this.akvClient.decrypt(EncryptionAlgorithm.fromString(algorithm), cipherText).getPlainText(); + } +} diff --git a/core/src/main/java/com/onelogin/saml2/model/hsm/HSM.java b/core/src/main/java/com/onelogin/saml2/model/hsm/HSM.java new file mode 100644 index 00000000..7f7cfdda --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/model/hsm/HSM.java @@ -0,0 +1,44 @@ +package com.onelogin.saml2.model.hsm; + +public abstract class HSM { + /** + * Sets the client to connect to the Azure Key Vault. + */ + public abstract void setClient(); + + /** + * Wraps a key with a particular algorithm using the HSM + * + * @param algorithm The algorithm to use to wrap the key. + * @param key The key to wrap + * @return A wrapped key. + */ + public abstract byte[] wrapKey(String algorithm, byte[] key); + + /** + * Unwraps a key with a particular algorithm using the HSM. + * + * @param algorithmUrl The algorithm URL to use to unwrap the key. + * @param wrappedKey The key to unwrap + * @return An unwrapped key. + */ + public abstract byte[] unwrapKey(String algorithmUrl, byte[] wrappedKey); + + /** + * Encrypts an array of bytes with a particular algorithm using the HSM. + * + * @param algorithm The algorithm to use for encryption. + * @param plainText The array of bytes to encrypt. + * @return An encrypted array of bytes. + */ + public abstract byte[] encrypt(String algorithm, byte[] plainText); + + /** + * Decrypts an array of bytes with a particular algorithm using the HSM. + * + * @param algorithm The algorithm to use for decryption. + * @param cipherText The encrypted array of bytes. + * @return A decrypted array of bytes. + */ + public abstract byte[] decrypt(String algorithm, byte[] cipherText); +} diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index e49eb7d4..a47c273a 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -7,6 +7,8 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; + +import com.onelogin.saml2.model.hsm.HSM; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -42,6 +44,7 @@ public class Saml2Settings { private X509Certificate spX509cert = null; private X509Certificate spX509certNew = null; private PrivateKey spPrivateKey = null; + private HSM hsm = null; // IdP private String idpEntityId = ""; @@ -362,6 +365,13 @@ public String getUniqueIDPrefix() { return this.uniqueIDPrefix; } + /** + * @return The HSM setting value. + */ + public HSM getHsm() { + return this.hsm; + } + /** * @return if the debug is active or not */ @@ -389,6 +399,15 @@ public void setDebug(boolean debug) { this.debug = debug; } + /** + * Sets the HSM setting value. + * + * @param hsm The HSM object to be set. + */ + public void setHsm(HSM hsm) { + this.hsm = hsm; + } + /** * Set the spEntityId setting value * @@ -911,12 +930,8 @@ public List checkSPSettings() { LOGGER.error(errorMsg); } - if ((this.getAuthnRequestsSigned() == true || - this.getLogoutRequestSigned() == true || - this.getLogoutResponseSigned() == true || - this.getWantAssertionsEncrypted() == true || - this.getWantNameIdEncrypted() == true) - && this.checkSPCerts() == false) { + if (this.getHsm() == null && (this.getAuthnRequestsSigned() || this.getLogoutRequestSigned() + || this.getLogoutResponseSigned() || this.getWantAssertionsEncrypted() || this.getWantNameIdEncrypted()) && !this.checkSPCerts()) { errorMsg = "sp_cert_not_found_and_required"; errors.add(errorMsg); LOGGER.error(errorMsg); @@ -942,7 +957,7 @@ public List checkSPSettings() { */ if (contact.getEmailAddress().isEmpty() || contact.getGivenName().isEmpty()) { - errorMsg = "contact_not_enought_data"; + errorMsg = "contact_not_enough_data"; errors.add(errorMsg); LOGGER.error(errorMsg); } @@ -951,7 +966,13 @@ public List checkSPSettings() { Organization org = this.getOrganization(); if (org != null && (org.getOrgDisplayName().isEmpty() || org.getOrgName().isEmpty() || org.getOrgUrl().isEmpty())) { - errorMsg = "organization_not_enought_data"; + errorMsg = "organization_not_enough_data"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + + if (this.getHsm() != null && this.getSPkey() != null) { + errorMsg = "use_either_hsm_or_private_key"; errors.add(errorMsg); LOGGER.error(errorMsg); } diff --git a/core/src/main/java/com/onelogin/saml2/util/Constants.java b/core/src/main/java/com/onelogin/saml2/util/Constants.java index 2677da92..cb22b2f9 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Constants.java +++ b/core/src/main/java/com/onelogin/saml2/util/Constants.java @@ -110,6 +110,9 @@ public final class Constants { public static String AES128_CBC = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; public static String AES192_CBC = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; public static String AES256_CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; + public static String A128KW = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public static String A192KW = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public static String A256KW = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; public static String RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; public static String RSA_OAEP_MGF1P = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; 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 0daa7a69..883ea55d 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -43,6 +43,7 @@ import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; @@ -61,6 +62,7 @@ import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactoryConfigurationException; +import com.onelogin.saml2.model.hsm.HSM; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -105,7 +107,7 @@ public final class Util { * Private property to construct a logger for this class. */ 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(); public static final String UNIQUE_ID_PREFIX = "ONELOGIN_"; @@ -943,7 +945,7 @@ public static boolean validateSign(final Document doc, final List getSignatureData(Node signNode, String alg) { } signatureData.put("signature", signature); - + String extractedFingerprint = null; X509Certificate extractedCert = null; KeyInfo keyInfo = signature.getKeyInfo(); @@ -1161,42 +1163,87 @@ public static void decryptElement(Element encryptedDataElement, PrivateKey input XMLCipher xmlCipher = XMLCipher.getInstance(); xmlCipher.init(XMLCipher.DECRYPT_MODE, null); - /* Check if we have encryptedData with a KeyInfo that contains a RetrievalMethod to obtain the EncryptedKey. - xmlCipher is not able to handle that so we move the EncryptedKey inside the KeyInfo element and - replacing the RetrievalMethod. - */ + validateEncryptedData(encryptedDataElement); - NodeList keyInfoInEncData = encryptedDataElement.getElementsByTagNameNS(Constants.NS_DS, "KeyInfo"); - if (keyInfoInEncData.getLength() == 0) { - throw new ValidationError("No KeyInfo inside EncryptedData element", ValidationError.KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA); - } + xmlCipher.setKEK(inputKey); + xmlCipher.doFinal(encryptedDataElement.getOwnerDocument(), encryptedDataElement, false); + } catch (Exception e) { + LOGGER.warn("Error executing decryption: " + e.getMessage(), e); + } + } - NodeList childs = keyInfoInEncData.item(0).getChildNodes(); - for (int i=0; i < childs.getLength(); i++) { - if (childs.item(i).getLocalName() != null && childs.item(i).getLocalName().equals("RetrievalMethod")) { - Element retrievalMethodElem = (Element)childs.item(i); - if (!retrievalMethodElem.getAttribute("Type").equals("http://www.w3.org/2001/04/xmlenc#EncryptedKey")) { - throw new ValidationError("Unsupported Retrieval Method found", ValidationError.UNSUPPORTED_RETRIEVAL_METHOD); - } + /** + * Decrypts the encrypted element using an HSM. + * + * @param encryptedDataElement The encrypted element. + * @param hsm The HSM object. + * + * @throws Exception + */ + public static void decryptUsingHsm(Element encryptedDataElement, HSM hsm) { + try { + validateEncryptedData(encryptedDataElement); - String uri = retrievalMethodElem.getAttribute("URI").substring(1); + XMLCipher xmlCipher = XMLCipher.getInstance(); + xmlCipher.init(XMLCipher.DECRYPT_MODE, null); - NodeList encryptedKeyNodes = ((Element) encryptedDataElement.getParentNode()).getElementsByTagNameNS(Constants.NS_XENC, "EncryptedKey"); - for (int j=0; j < encryptedKeyNodes.getLength(); j++) { - if (((Element)encryptedKeyNodes.item(j)).getAttribute("Id").equals(uri)) { - keyInfoInEncData.item(0).replaceChild(encryptedKeyNodes.item(j), childs.item(i)); - } - } - } - } + hsm.setClient(); - xmlCipher.setKEK(inputKey); + NodeList encryptedKeyNodes = ((Element) encryptedDataElement.getParentNode()).getElementsByTagNameNS(Constants.NS_XENC, "EncryptedKey"); + EncryptedKey encryptedKey = xmlCipher.loadEncryptedKey((Element) encryptedKeyNodes.item(0)); + byte[] encryptedBytes = base64decoder(encryptedKey.getCipherData().getCipherValue().getValue()); + + byte[] decryptedKey = hsm.unwrapKey(encryptedKey.getEncryptionMethod().getAlgorithm(), encryptedBytes); + + SecretKey encryptionKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES"); + + xmlCipher.init(XMLCipher.DECRYPT_MODE, encryptionKey); + xmlCipher.setKEK(encryptionKey); xmlCipher.doFinal(encryptedDataElement.getOwnerDocument(), encryptedDataElement, false); } catch (Exception e) { LOGGER.warn("Error executing decryption: " + e.getMessage(), e); } } + /** + * Validates the encrypted data and checks whether it contains a retrieval + * method to obtain the encrypted key or not. + * + * @param encryptedDataElement The encrypted element. + * + * @throws ValidationError + */ + private static void validateEncryptedData(Element encryptedDataElement) throws ValidationError { + /* Check if we have encryptedData with a KeyInfo that contains a RetrievalMethod to obtain the EncryptedKey. + xmlCipher is not able to handle that so we move the EncryptedKey inside the KeyInfo element and + replacing the RetrievalMethod. + */ + + NodeList keyInfoInEncData = encryptedDataElement.getElementsByTagNameNS(Constants.NS_DS, "KeyInfo"); + if (keyInfoInEncData.getLength() == 0) { + throw new ValidationError("No KeyInfo inside EncryptedData element", ValidationError.KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA); + } + + NodeList childs = keyInfoInEncData.item(0).getChildNodes(); + for (int i=0; i < childs.getLength(); i++) { + if (childs.item(i).getLocalName() != null && childs.item(i).getLocalName().equals("RetrievalMethod")) { + Element retrievalMethodElem = (Element)childs.item(i); + if (!retrievalMethodElem.getAttribute("Type").equals("http://www.w3.org/2001/04/xmlenc#EncryptedKey")) { + throw new ValidationError("Unsupported Retrieval Method found", ValidationError.UNSUPPORTED_RETRIEVAL_METHOD); + } + + String uri = retrievalMethodElem.getAttribute("URI").substring(1); + + NodeList encryptedKeyNodes = ((Element) encryptedDataElement.getParentNode()).getElementsByTagNameNS(Constants.NS_XENC, "EncryptedKey"); + for (int j=0; j < encryptedKeyNodes.getLength(); j++) { + if (((Element)encryptedKeyNodes.item(j)).getAttribute("Id").equals(uri)) { + keyInfoInEncData.item(0).replaceChild(encryptedKeyNodes.item(j), childs.item(i)); + } + } + } + } + } + /** * Clone a Document object. * diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java index 93224724..366614bb 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java @@ -20,6 +20,7 @@ import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.SchemaFactory; import com.onelogin.saml2.util.Util; +import com.onelogin.saml2.model.hsm.AzureKeyVault; /** * Tests the com.onelogin.saml2.settings.Saml2Settings class @@ -35,7 +36,7 @@ public class Saml2SettingsTest { @Test public void testIsStrict() { Saml2Settings settings = new Saml2Settings(); - + assertTrue(settings.isStrict()); settings.setStrict(false); assertFalse(settings.isStrict()); @@ -52,14 +53,14 @@ public void testIsStrict() { @Test public void testIsDebugActive() { Saml2Settings settings = new Saml2Settings(); - + assertFalse(settings.isDebugActive()); settings.setDebug(true); assertTrue(settings.isDebugActive()); settings.setDebug(false); assertFalse(settings.isDebugActive()); } - + /** * Tests the checkIdPSettings method of the Saml2Settings * Case: Check that all possible IdP errors are found @@ -113,8 +114,8 @@ public void testCheckSPSettingsAllErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("sp_entityId_not_found")); assertThat(settingsErrors, hasItem("sp_acs_not_found")); assertThat(settingsErrors, hasItem("sp_cert_not_found_and_required")); - assertThat(settingsErrors, hasItem("contact_not_enought_data")); - assertThat(settingsErrors, hasItem("organization_not_enought_data")); + assertThat(settingsErrors, hasItem("contact_not_enough_data")); + assertThat(settingsErrors, hasItem("organization_not_enough_data")); } /** @@ -150,8 +151,8 @@ public void testCheckSettingsAllErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("sp_entityId_not_found")); assertThat(settingsErrors, hasItem("sp_acs_not_found")); assertThat(settingsErrors, hasItem("sp_cert_not_found_and_required")); - assertThat(settingsErrors, hasItem("contact_not_enought_data")); - assertThat(settingsErrors, hasItem("organization_not_enought_data")); + assertThat(settingsErrors, hasItem("contact_not_enough_data")); + assertThat(settingsErrors, hasItem("organization_not_enough_data")); assertThat(settingsErrors, hasItem("idp_entityId_not_found")); assertThat(settingsErrors, hasItem("idp_sso_url_invalid")); assertThat(settingsErrors, hasItem("idp_cert_or_fingerprint_not_found_and_required")); @@ -176,7 +177,7 @@ public void testCheckSettingsIdPErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("idp_sso_url_invalid")); assertThat(settingsErrors, hasItem("idp_cert_or_fingerprint_not_found_and_required")); assertThat(settingsErrors, hasItem("idp_cert_not_found_and_required")); - + settings.setSPValidationOnly(true); settingsErrors = settings.checkSettings(); assertTrue(settingsErrors.isEmpty()); @@ -185,9 +186,9 @@ public void testCheckSettingsIdPErrors() throws IOException, Error { /** * Tests the checkIdpSettings method of the {@link Saml2Settings} * Case: Multiple certs defined. - * + * * @throws Exception - * + * * @see com.onelogin.saml2.settings.Saml2Settings#checkIdPSettings */ @Test @@ -213,6 +214,45 @@ public void testCheckSettingsOk() throws IOException, Error { assertTrue(settingsErrors.isEmpty()); } + /** + * Tests the checkSpSettings() method of the Saml2Settings + * Case: Setting the HSM (Azure Key Vault) as part of the SAML settings + * should not throw the sp_cert_not_found_and_required and + * use_either_hsm_or_private_key errors. + * + * @throws IOException + * @throws Error + * + * @see com.onelogin.saml2.settings.Saml2Settings#checkSPSettings + */ + @Test + public void testCheckSpSettingsWhenSettingHsm() throws IOException, Error { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.hsm.properties").build(); + settings.setHsm(new AzureKeyVault("", "", "", "")); + + List settingsErrors = settings.checkSettings(); + assertTrue(settingsErrors.isEmpty()); + } + + /** + * Tests the checkSpSettings() method of the Saml2Settings + * Case: Setting both the HSM (Azure Key Vault) and the private key will + * throw an error. + * + * @throws IOException + * @throws Error + * + * @see com.onelogin.saml2.settings.Saml2Settings#checkSPSettings + */ + @Test + public void testCheckSpSettingsWhenSettingBothHsmAndPrivateKey() throws IOException, Error { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + settings.setHsm(new AzureKeyVault("", "", "", "")); + + List settingsErrors = settings.checkSettings(); + assertThat(settingsErrors, hasItem("use_either_hsm_or_private_key")); + } + /** * Tests the getSPMetadata method of the Saml2Settings * * Case Unsigned metadata @@ -232,16 +272,16 @@ public void testGetSPMetadataUnsigned() throws Exception { assertEquals("md:EntityDescriptor", metadataDoc.getDocumentElement().getNodeName()); assertEquals("md:SPSSODescriptor", metadataDoc.getDocumentElement().getFirstChild().getNodeName()); - + assertTrue(Util.validateXML(metadataDoc, SchemaFactory.SAML_SCHEMA_METADATA_2_0)); - + assertThat(metadataStr, containsString(""))); assertThat(metadataStr, containsString("")); - assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("")); assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } @@ -264,19 +304,19 @@ public void testGetSPMetadataUnsignedNoSLS() throws Exception { assertEquals("md:EntityDescriptor", metadataDoc.getDocumentElement().getNodeName()); assertEquals("md:SPSSODescriptor", metadataDoc.getDocumentElement().getFirstChild().getNodeName()); - + assertTrue(Util.validateXML(metadataDoc, SchemaFactory.SAML_SCHEMA_METADATA_2_0)); - + assertThat(metadataStr, containsString(""))); assertThat(metadataStr, containsString("")); - assertThat(metadataStr, not(containsString(""))); + assertThat(metadataStr, not(containsString(""))); assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } - + /** * Tests the getSPMetadata method of the Saml2Settings * * Case Signed metadata @@ -297,14 +337,14 @@ public void testGetSPMetadataSigned() throws Exception { Node ds_signature_metadata = metadataDoc.getFirstChild().getFirstChild(); assertEquals(Constants.C14NEXC, ds_signature_metadata.getFirstChild().getFirstChild().getAttributes().getNamedItem("Algorithm").getNodeValue()); - + assertEquals(Constants.RSA_SHA512, ds_signature_metadata.getFirstChild().getFirstChild().getNextSibling().getAttributes().getNamedItem("Algorithm").getNodeValue()); assertEquals(Constants.SHA1, ds_signature_metadata.getFirstChild().getFirstChild().getNextSibling().getNextSibling().getFirstChild().getNextSibling().getAttributes().getNamedItem("Algorithm").getNodeValue()); assertEquals("md:SPSSODescriptor", metadataDoc.getDocumentElement().getFirstChild().getNextSibling().getNodeName()); - + assertTrue(Util.validateXML(metadataDoc, SchemaFactory.SAML_SCHEMA_METADATA_2_0)); - + assertThat(metadataStr, containsString("")); - assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("")); assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } - + /** * Tests the validateMetadata method of the Saml2Settings * Case Valid @@ -332,7 +372,7 @@ public void testGetSPMetadataSigned() throws Exception { public void testValidateMetadataValid() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); String metadataStr = settings.getSPMetadata(); - + List errors = Saml2Settings.validateMetadata(metadataStr); assertTrue(errors.isEmpty()); } @@ -355,7 +395,7 @@ public void testValidateMetadataInvalidXML() throws Exception { assertFalse(errors.isEmpty()); assertTrue(errors.contains("Invalid SAML Metadata. Not match the saml-schema-metadata-2.0.xsd")); } - + /** * Tests the validateMetadata method of the Saml2Settings * Case Invalid: noEntityDescriptor_xml @@ -440,7 +480,7 @@ public void testValidateMetadataExpired() throws Exception { Calendar validUntilTime = Calendar.getInstance(); validUntilTime.add(Calendar.DAY_OF_YEAR, -2); - + Metadata metadataObj = new Metadata(settings, validUntilTime, null); String metadataStr = metadataObj.getMetadataString(); metadataStr = metadataStr.replace("cacheDuration=\"PT604800S\"", ""); diff --git a/core/src/test/resources/config/config.hsm.properties b/core/src/test/resources/config/config.hsm.properties new file mode 100644 index 00000000..9bec319b --- /dev/null +++ b/core/src/test/resources/config/config.hsm.properties @@ -0,0 +1,26 @@ +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp + +# Identity Provider Data that we want connect with our SP +# Identifier of the IdP entity (must be a URI) +onelogin.saml2.idp.entityid = http://idp.example.com/ + +# SSO endpoint info of the IdP. (Authentication Request protocol) +# URL Target of the IdP where the SP will send the Authentication Request Message +onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php + +# SLO endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Request +onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php + +# Public x509 certificate of the IdP +onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==\n-----END CERTIFICATE----- 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 01b372b3..0900d755 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -291,7 +291,7 @@ public void testConstructorInvalidSettings() throws IOException, SettingsExcepti Saml2Settings settings = new SettingsBuilder().fromFile("config/config.sperrors.properties").build(); expectedEx.expect(SettingsException.class); - expectedEx.expectMessage("Invalid settings: sp_entityId_not_found, sp_acs_not_found, sp_cert_not_found_and_required, contact_not_enought_data, organization_not_enought_data, idp_cert_or_fingerprint_not_found_and_required, idp_cert_not_found_and_required"); + expectedEx.expectMessage("Invalid settings: sp_entityId_not_found, sp_acs_not_found, sp_cert_not_found_and_required, contact_not_enough_data, organization_not_enough_data, idp_cert_or_fingerprint_not_found_and_required, idp_cert_not_found_and_required"); new Auth(settings, request, response); } From ca62eac7701fe4ba4f07fd549e538c5b9cf84d20 Mon Sep 17 00:00:00 2001 From: Robert Buttigieg Date: Fri, 6 Nov 2020 07:53:11 +0100 Subject: [PATCH 039/133] 1. Updated the dependency-check plugin to version 6.0.3. 2. Removed any local suppressions since these false positives have been addressed in the above mentioned version. --- .nvd-suppressions.xml | 10 ---------- pom.xml | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.nvd-suppressions.xml b/.nvd-suppressions.xml index 15890244..3af0cdfa 100644 --- a/.nvd-suppressions.xml +++ b/.nvd-suppressions.xml @@ -1,13 +1,3 @@ - - - .*\bmsal4j-1.6.1\.jar - cpe:/a:http_authentication_library_project:http_authentication_library - - - - .*\boauth2-oidc-sdk-6.14\.jar - cpe:/a:openid:openid - diff --git a/pom.xml b/pom.xml index e9223dc5..813e7631 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.owasp dependency-check-maven - 6.0.2 + 6.0.3 7 From e9b78ad51fa0950c12614b25e0ff1612c581277d Mon Sep 17 00:00:00 2001 From: rolando ramirez hidalgo Date: Mon, 16 Nov 2020 12:15:05 -0500 Subject: [PATCH 040/133] Read digest algorithm from configuration --- .../com/onelogin/saml2/settings/SettingsBuilder.java | 7 ++++++- .../saml2/test/settings/Saml2SettingsTest.java | 2 +- .../saml2/test/settings/SettingBuilderTest.java | 12 ++++++++++++ .../src/test/resources/config/config.adfs.properties | 9 ++++++++- core/src/test/resources/config/config.all.properties | 12 ++++++------ .../config/config.allowduplicatednames.properties | 7 +++++++ .../resources/config/config.different.properties | 7 +++++++ .../config/config.knownIdpPrivateKey.properties | 7 +++++++ core/src/test/resources/config/config.my.properties | 7 +++++++ .../config/config.mywithmulticert.properties | 7 +++++++ .../resources/config/config.mywithnocert.properties | 7 +++++++ .../resources/config/config.newattack.properties | 7 +++++++ .../resources/config/config.newattack2.properties | 7 +++++++ .../resources/config/config.samecerts.properties | 7 +++++++ .../config/config.somevaluesempty.properties | 7 +++++++ 15 files changed, 103 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 9a706e5a..802ed322 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -98,6 +98,7 @@ public class SettingsBuilder { public final static String SECURITY_REQUESTED_AUTHNCONTEXTCOMPARISON = "onelogin.saml2.security.requested_authncontextcomparison"; public final static String SECURITY_WANT_XML_VALIDATION = "onelogin.saml2.security.want_xml_validation"; public final static String SECURITY_SIGNATURE_ALGORITHM = "onelogin.saml2.security.signature_algorithm"; + public final static String SECURITY_DIGEST_ALGORITHM = "onelogin.saml2.security.digest_algorithm"; public final static String SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO = "onelogin.saml2.security.reject_unsolicited_responses_with_inresponseto"; public final static String SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.security.allow_duplicated_attribute_name"; @@ -216,7 +217,7 @@ public SettingsBuilder fromValues(Map samlData, KeyStoreSettings /** * Builds the Saml2Settings object. Read the Properties object and set all the * SAML settings - * + * * @return the Saml2Settings object with all the SAML settings loaded * */ @@ -365,6 +366,10 @@ private void loadSecuritySetting() { if (signatureAlgorithm != null && !signatureAlgorithm.isEmpty()) saml2Setting.setSignatureAlgorithm(signatureAlgorithm); + String digestAlgorithm = loadStringProperty(SECURITY_DIGEST_ALGORITHM); + if (digestAlgorithm != null && !digestAlgorithm.isEmpty()) + saml2Setting.setDigestAlgorithm(digestAlgorithm); + Boolean rejectUnsolicitedResponsesWithInResponseTo = loadBooleanProperty(SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO); if (rejectUnsolicitedResponsesWithInResponseTo != null) { saml2Setting.setRejectUnsolicitedResponsesWithInResponseTo(rejectUnsolicitedResponsesWithInResponseTo); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java index 93224724..14640e5d 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java @@ -299,7 +299,7 @@ public void testGetSPMetadataSigned() throws Exception { assertEquals(Constants.C14NEXC, ds_signature_metadata.getFirstChild().getFirstChild().getAttributes().getNamedItem("Algorithm").getNodeValue()); assertEquals(Constants.RSA_SHA512, ds_signature_metadata.getFirstChild().getFirstChild().getNextSibling().getAttributes().getNamedItem("Algorithm").getNodeValue()); - assertEquals(Constants.SHA1, ds_signature_metadata.getFirstChild().getFirstChild().getNextSibling().getNextSibling().getFirstChild().getNextSibling().getAttributes().getNamedItem("Algorithm").getNodeValue()); + assertEquals(Constants.SHA512, ds_signature_metadata.getFirstChild().getFirstChild().getNextSibling().getNextSibling().getFirstChild().getNextSibling().getAttributes().getNamedItem("Algorithm").getNodeValue()); assertEquals("md:SPSSODescriptor", metadataDoc.getDocumentElement().getFirstChild().getNextSibling().getNodeName()); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java index 1d46d610..956a343d 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java @@ -158,6 +158,7 @@ public void testLoadFromFileEmpty() throws IOException, CertificateException, UR assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); assertNull(setting.getOrganization()); @@ -213,6 +214,7 @@ public void testLoadFromFileMinProp() throws IOException, CertificateException, assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); assertNull(setting.getOrganization()); @@ -273,6 +275,7 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); @@ -338,6 +341,7 @@ public void testLoadFromFileCertString() throws IOException, CertificateExceptio assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); @@ -392,6 +396,7 @@ public void testLoadFromFileContactString() throws IOException, CertificateExcep assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); @@ -504,6 +509,7 @@ public void testLoadFromFileSomeEmptyProp() throws IOException, CertificateExcep assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); assertNull(setting.getOrganization()); @@ -560,6 +566,7 @@ public void testLoadFromFileDifferentProp() throws IOException, CertificateExcep assertEquals("minimum", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); Organization org = new Organization("SP Java", "", ""); @@ -652,6 +659,7 @@ public void testFromProperties() throws IOException, Error, CertificateException assertEquals("exact", setting2.getRequestedAuthnContextComparison()); assertTrue(setting2.getWantXMLValidation()); assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); assertFalse(setting2.getSignMetadata()); assertNull(setting2.getOrganization()); @@ -713,6 +721,7 @@ public void testLoadFromValues() throws Exception { samlData.put(SECURITY_REQUESTED_AUTHNCONTEXTCOMPARISON, "exact"); samlData.put(SECURITY_WANT_XML_VALIDATION, "true"); samlData.put(SECURITY_SIGNATURE_ALGORITHM, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); + samlData.put(SECURITY_DIGEST_ALGORITHM, "http://www.w3.org/2001/04/xmlenc#sha512"); // Compress samlData.put(COMPRESS_REQUEST, "false"); @@ -777,6 +786,7 @@ public void testLoadFromValues() throws Exception { assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); assertFalse(setting.getWantNameId()); @@ -868,6 +878,7 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(SECURITY_REQUESTED_AUTHNCONTEXTCOMPARISON, "exact"); samlData.put(SECURITY_WANT_XML_VALIDATION, true); samlData.put(SECURITY_SIGNATURE_ALGORITHM, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); + samlData.put(SECURITY_DIGEST_ALGORITHM, "http://www.w3.org/2001/04/xmlenc#sha512"); // Compress samlData.put(COMPRESS_REQUEST, "false"); @@ -924,6 +935,7 @@ public void testLoadFromValuesWithObjects() throws Exception { assertEquals("exact", setting.getRequestedAuthnContextComparison()); assertTrue(setting.getWantXMLValidation()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); assertFalse(setting.getWantNameId()); diff --git a/core/src/test/resources/config/config.adfs.properties b/core/src/test/resources/config/config.adfs.properties index b695f883..96540e35 100644 --- a/core/src/test/resources/config/config.adfs.properties +++ b/core/src/test/resources/config/config.adfs.properties @@ -36,7 +36,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9LcGorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6wsKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786UlujTvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXBBgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6RnfdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXzVYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRXEouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKCUn0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x9+QU/vJr7lMLzdoy-----END PRIVATE KEY----- @@ -124,6 +124,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index 2fbfa024..8e2c85ec 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -130,12 +130,12 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 -# Algorithm that the toolkit will use on signing process. Options: -# 'http://www.w3.org/2000/09/xmldsig#sha1' -# 'http://www.w3.org/2001/04/xmlenc#sha256' -# 'http://www.w3.org/2001/04/xmldsig-more#sha384' -# 'http://www.w3.org/2001/04/xmlenc#sha512' -onelogin.saml2.security.digest_algorithm = http://www.w3.org/2000/09/xmldsig#sha1 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization onelogin.saml2.organization.name = SP Java diff --git a/core/src/test/resources/config/config.allowduplicatednames.properties b/core/src/test/resources/config/config.allowduplicatednames.properties index 9c31a66e..8984f14e 100644 --- a/core/src/test/resources/config/config.allowduplicatednames.properties +++ b/core/src/test/resources/config/config.allowduplicatednames.properties @@ -127,6 +127,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.different.properties b/core/src/test/resources/config/config.different.properties index b48414c6..d8441d3f 100644 --- a/core/src/test/resources/config/config.different.properties +++ b/core/src/test/resources/config/config.different.properties @@ -127,6 +127,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java diff --git a/core/src/test/resources/config/config.knownIdpPrivateKey.properties b/core/src/test/resources/config/config.knownIdpPrivateKey.properties index 0c21f63e..e4a43f9f 100644 --- a/core/src/test/resources/config/config.knownIdpPrivateKey.properties +++ b/core/src/test/resources/config/config.knownIdpPrivateKey.properties @@ -131,6 +131,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.my.properties b/core/src/test/resources/config/config.my.properties index 2875ccae..c86a2915 100644 --- a/core/src/test/resources/config/config.my.properties +++ b/core/src/test/resources/config/config.my.properties @@ -124,6 +124,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.mywithmulticert.properties b/core/src/test/resources/config/config.mywithmulticert.properties index 490b7283..20a5c88d 100644 --- a/core/src/test/resources/config/config.mywithmulticert.properties +++ b/core/src/test/resources/config/config.mywithmulticert.properties @@ -126,6 +126,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.mywithnocert.properties b/core/src/test/resources/config/config.mywithnocert.properties index 099bcf0b..076de76f 100644 --- a/core/src/test/resources/config/config.mywithnocert.properties +++ b/core/src/test/resources/config/config.mywithnocert.properties @@ -121,6 +121,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.newattack.properties b/core/src/test/resources/config/config.newattack.properties index f01bfe42..fb916a8b 100644 --- a/core/src/test/resources/config/config.newattack.properties +++ b/core/src/test/resources/config/config.newattack.properties @@ -123,6 +123,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.newattack2.properties b/core/src/test/resources/config/config.newattack2.properties index 8d15b505..175407ff 100644 --- a/core/src/test/resources/config/config.newattack2.properties +++ b/core/src/test/resources/config/config.newattack2.properties @@ -120,6 +120,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.samecerts.properties b/core/src/test/resources/config/config.samecerts.properties index d91bb37e..fcd91490 100644 --- a/core/src/test/resources/config/config.samecerts.properties +++ b/core/src/test/resources/config/config.samecerts.properties @@ -120,6 +120,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.somevaluesempty.properties b/core/src/test/resources/config/config.somevaluesempty.properties index 7bd819f5..ca5897f9 100644 --- a/core/src/test/resources/config/config.somevaluesempty.properties +++ b/core/src/test/resources/config/config.somevaluesempty.properties @@ -127,6 +127,13 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' onelogin.saml2.security.signature_algorithm = +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = + # Organization onelogin.saml2.organization.name = onelogin.saml2.organization.displayname = From 1d798744944027aaae92801917364280b662733b Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 20 Nov 2020 17:13:26 +0100 Subject: [PATCH 041/133] Set SHA-256 as default alg in settings. Support Alg Deprecated rejection. Notify with Logger.info if sha-1 alg used on Signature --- README.md | 16 +++- .../onelogin/saml2/authn/SamlResponse.java | 6 +- .../onelogin/saml2/logout/LogoutRequest.java | 11 +++ .../onelogin/saml2/logout/LogoutResponse.java | 10 ++ .../saml2/settings/Saml2Settings.java | 20 +++- .../saml2/settings/SettingsBuilder.java | 9 +- .../java/com/onelogin/saml2/util/Util.java | 94 ++++++++++++++++++- .../saml2/test/authn/AuthnResponseTest.java | 49 ++++++++++ .../saml2/test/logout/LogoutRequestTest.java | 34 +++++++ .../saml2/test/logout/LogoutResponseTest.java | 37 ++++++++ .../onelogin/saml2/test/util/UtilsTest.java | 6 ++ .../metadata/signed_metadata_settings256.xml | 38 ++++++++ .../main/resources/onelogin.saml.properties | 14 ++- 13 files changed, 333 insertions(+), 11 deletions(-) create mode 100644 core/src/test/resources/data/metadata/signed_metadata_settings256.xml diff --git a/README.md b/README.md index e91b0eda..07a34129 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ onelogin.saml2.idp.x509cert = # let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512 # 'sha1' is the default value. # onelogin.saml2.idp.certfingerprint = -# onelogin.saml2.idp.certfingerprint_algorithm = sha1 +# onelogin.saml2.idp.certfingerprint_algorithm = sha256 # Security settings # @@ -342,7 +342,18 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' -onelogin.saml2.security.signature_algorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1 +onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256 + + +# Reject Signatures with deprecated algorithms (sha1) +onelogin.saml2.security.reject_deprecated_alg = true # Organization onelogin.saml2.organization.name = SP Java @@ -362,6 +373,7 @@ onelogin.saml2.contacts.support.email_address = support@example.com # onelogin.saml2.unique_id_prefix = _ ``` + ##### KeyStores The Auth constructor supports the ability to read SP public cert/private key from a KeyStore. A KeyStoreSettings object must be provided with the KeyStore, the Alias and the KeyEntry password. 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 29136337..7f38b81c 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -323,12 +323,14 @@ public boolean isValid(String requestId) { String fingerprint = settings.getIdpCertFingerprint(); String alg = settings.getIdpCertFingerprintAlgorithm(); - if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH)) { + Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); + + if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH, rejectDeprecatedAlg)) { throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE); } final Document documentToCheckAssertion = encrypted ? decryptedDocument : samlResponseDocument; - if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH)) { + if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH, rejectDeprecatedAlg)) { throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE); } } 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 a9431136..9ae8499c 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -456,6 +456,17 @@ public Boolean isValid() throws Exception { if (signAlg == null || signAlg.isEmpty()) { signAlg = Constants.RSA_SHA1; } + + if (signAlg.equals(Constants.RSA_SHA1)) { + Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); + if (rejectDeprecatedAlg) { + LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); + return false; + } else { + LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); + } + } + String relayState = request.getEncodedParameter("RelayState"); String signedQuery = "SAMLRequest=" + request.getEncodedParameter("SAMLRequest"); 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 6d2b990e..9cf14e7c 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -258,6 +258,16 @@ public Boolean isValid(String requestId) { signAlg = Constants.RSA_SHA1; } + if (signAlg.equals(Constants.RSA_SHA1)) { + Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); + if (rejectDeprecatedAlg) { + LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); + return false; + } else { + LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); + } + } + String signedQuery = "SAMLResponse=" + request.getEncodedParameter("SAMLResponse"); String relayState = request.getEncodedParameter("RelayState"); diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index a47c273a..f5c8565b 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -76,6 +76,7 @@ public class Saml2Settings { private String digestAlgorithm = Constants.SHA1; private boolean rejectUnsolicitedResponsesWithInResponseTo = false; private boolean allowRepeatAttributeName = false; + private boolean rejectDeprecatedAlg = false; private String uniqueIDPrefix = null; // Compress @@ -140,10 +141,17 @@ public final String getSpNameIDFormat() { /** * @return the allowRepeatAttributeName setting value */ - public boolean isAllowRepeatAttributeName () { + public boolean isAllowRepeatAttributeName() { return allowRepeatAttributeName; } + /** + * @return the rejectDeprecatedAlg setting value + */ + public boolean getRejectDeprecatedAlg() { + return rejectDeprecatedAlg; + } + /** * @return the spX509cert setting value */ @@ -478,6 +486,16 @@ public void setAllowRepeatAttributeName (boolean allowRepeatAttributeName) { this.allowRepeatAttributeName = allowRepeatAttributeName; } + /** + * Set the rejectDeprecatedAlg setting value + * + * @param rejectDeprecatedAlg + * the rejectDeprecatedAlg value to be set + */ + public void setRejectDeprecatedAlg (boolean rejectDeprecatedAlg) { + this.rejectDeprecatedAlg = rejectDeprecatedAlg; + } + /** * Set the spX509cert setting value provided as X509Certificate object * diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 802ed322..1b30a4ec 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -101,6 +101,7 @@ public class SettingsBuilder { public final static String SECURITY_DIGEST_ALGORITHM = "onelogin.saml2.security.digest_algorithm"; public final static String SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO = "onelogin.saml2.security.reject_unsolicited_responses_with_inresponseto"; public final static String SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.security.allow_duplicated_attribute_name"; + public final static String SECURITY_REJECT_DEPRECATED_ALGORITHM = "onelogin.saml2.security.reject_deprecated_alg"; // Compress public final static String COMPRESS_REQUEST = "onelogin.saml2.compress.request"; @@ -376,8 +377,14 @@ private void loadSecuritySetting() { } Boolean allowRepeatAttributeName = loadBooleanProperty(SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY); - if (allowRepeatAttributeName != null) + if (allowRepeatAttributeName != null) { saml2Setting.setAllowRepeatAttributeName(allowRepeatAttributeName); + } + + Boolean rejectDeprecatedAlg = loadBooleanProperty(SECURITY_REJECT_DEPRECATED_ALGORITHM); + if (rejectDeprecatedAlg != null) { + saml2Setting.setRejectDeprecatedAlg(rejectDeprecatedAlg); + } } /** 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 883ea55d..6b72894b 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -923,13 +923,30 @@ public static boolean validateSign(final Document doc, final X509Certificate cer */ public static boolean validateSign(final Document doc, final List certList, final String fingerprint, final String alg, final String xpath) { + return validateSign(doc, certList, fingerprint,alg, xpath, false); + } + + /** + * Validate the signature pointed to by the xpath + * + * @param doc The document we should validate + * @param certList The public certificates + * @param fingerprint The fingerprint of the public certificate + * @param alg The signature algorithm method + * @param xpath the xpath of the ds:Signture node to validate + * @param rejectDeprecatedAlg Flag to invalidate or not Signatures with deprecated alg + * + * @return True if the signature exists and is valid, false otherwise. + */ + public static boolean validateSign(final Document doc, final List certList, final String fingerprint, + final String alg, final String xpath, final Boolean rejectDeprecatedAlg) { try { final NodeList signatures = query(doc, xpath); if (signatures.getLength() == 1) { final Node signNode = signatures.item(0); - Map signatureData = getSignatureData(signNode, alg); + Map signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg); if (signatureData.isEmpty()) { return false; } @@ -984,6 +1001,26 @@ public static boolean validateSign(final Document doc, final List 0) { for (int i = 0; i < signNodesToValidate.getLength(); i++) { Node signNode = signNodesToValidate.item(i); - if (!validateSignNode(signNode, cert, fingerprint, alg)) { + if (!validateSignNode(signNode, cert, fingerprint, alg, rejectDeprecatedAlg)) { return false; } } @@ -1026,6 +1063,26 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S * @return True if the sign is valid, false otherwise. */ private static Map getSignatureData(Node signNode, String alg) { + return getSignatureData(signNode, alg, false); + } + + /** + * Validate signature (Metadata). + * + * @param doc + * The document we should validate + * @param cert + * The public certificate + * @param fingerprint + * The fingerprint of the public certificate + * @param alg + * The signature algorithm method + * @param rejectDeprecatedAlg + * Flag to invalidate or not Signatures with deprecated alg + * + * @return True if the sign is valid, false otherwise. + */ + private static Map getSignatureData(Node signNode, String alg, Boolean rejectDeprecatedAlg) { Map signatureData = new HashMap<>(); try { Element sigElement = (Element) signNode; @@ -1036,6 +1093,15 @@ private static Map getSignatureData(Node signNode, String alg) { throw new Exception(sigMethodAlg + " is not a valid supported algorithm"); } + if (sigMethodAlg.equals(Constants.RSA_SHA1)) { + if (rejectDeprecatedAlg) { + LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); + return signatureData; + } else { + LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); + } + } + signatureData.put("signature", signature); String extractedFingerprint = null; @@ -1073,7 +1139,29 @@ private static Map getSignatureData(Node signNode, String alg) { * @throws Exception */ public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg) { - Map signatureData = getSignatureData(signNode, alg); + return validateSignNode(signNode, cert, fingerprint, alg, false); + } + + /** + * Validate signature of the Node. + * + * @param signNode + * The document we should validate + * @param cert + * The public certificate + * @param fingerprint + * The fingerprint of the public certificate + * @param alg + * The signature algorithm method + * @param rejectDeprecatedAlg + * Flag to invalidate or not Signatures with deprecated alg + * + * @return True if the sign is valid, false otherwise. + * + * @throws Exception + */ + public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg, Boolean rejectDeprecatedAlg) { + Map signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg); if (signatureData.isEmpty()) { return false; } 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 bfb2af8e..cae24ce2 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 @@ -5,6 +5,7 @@ import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -2409,6 +2410,54 @@ public void testIsValid_signedEncryptedAssertion() throws Exception { assertResponseValid(settings, samlResponseEncoded, true, false, "The Message of the Response is not signed and the SP requires it"); } + /** + * Tests the isValid method of SamlResponse + * Case: Signed with deprecated method and flag enabled + * + * @throws ValidationError + * @throws SettingsException + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * @throws Error + * + * @see com.onelogin.saml2.authn.SamlResponse#isValid + */ + @Test + public void testIsInValidSignWithDeprecatedAlg() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + settings.setWantAssertionsSigned(false); + settings.setWantMessagesSigned(false); + String samlResponseEncoded = Util.getFileAsString("data/responses/signed_message_response.xml.base64"); + + settings.setStrict(true); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertTrue(samlResponse.isValid()); + + settings.setRejectDeprecatedAlg(true); + SamlResponse samlResponse2 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse2.isValid()); + + settings.setRejectDeprecatedAlg(false); + samlResponseEncoded = Util.getFileAsString("data/responses/signed_assertion_response.xml.base64"); + SamlResponse samlResponse3 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertTrue(samlResponse3.isValid()); + + settings.setRejectDeprecatedAlg(true); + SamlResponse samlResponse4 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse4.isValid()); + + settings.setRejectDeprecatedAlg(false); + samlResponseEncoded = Util.getFileAsString("data/responses/double_signed_response.xml.base64"); + SamlResponse samlResponse5 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertTrue(samlResponse5.isValid()); + + settings.setRejectDeprecatedAlg(true); + SamlResponse samlResponse6 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse6.isValid()); + } + /** * Tests the isValid method of SamlResponse * Case: valid sign response / sign assertion / both signed diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index a8e73d76..cf9fab1f 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -766,6 +766,40 @@ public void testIsInValidSign() throws Exception { assertEquals("In order to validate the sign on the Logout Request, the x509cert of the IdP is required", logoutRequest.getError()); } + /** + * Tests the isValid method of LogoutRequest + * Case: Signed with deprecated method and flag enabled + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutRequest#isValid + */ + @Test + public void testIsInValidSignWithDeprecatedAlg() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + settings.setStrict(false); + settings.setWantMessagesSigned(true); + + final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; + String samlRequestEncoded = "lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE="; + String relayState = "_1037fbc88ec82ce8e770b2bed1119747bb812a07e6"; + String sigAlg = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + String signature = "XCwCyI5cs7WhiJlB5ktSlWxSBxv+6q2xT3c8L7dLV6NQG9LHWhN7gf8qNsahSXfCzA0Ey9dp5BQ0EdRvAk2DIzKmJY6e3hvAIEp1zglHNjzkgcQmZCcrkK9Czi2Y1WkjOwR/WgUTUWsGJAVqVvlRZuS3zk3nxMrLH6f7toyvuJc="; + + HttpRequest httpRequest = new HttpRequest(requestURL, (String)null) + .addParameter("SAMLRequest", samlRequestEncoded) + .addParameter("RelayState", relayState) + .addParameter("SigAlg", sigAlg) + .addParameter("Signature", signature); + LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); + logoutRequest = new LogoutRequest(settings, httpRequest); + assertTrue(logoutRequest.isValid()); + + settings.setRejectDeprecatedAlg(true); + LogoutRequest logoutRequest2 = new LogoutRequest(settings, httpRequest); + assertFalse(logoutRequest2.isValid()); + } + /** * Tests the isValid method of LogoutRequest * Case: No SAML Logout Request diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index f6f14c58..fe04cb7c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -552,6 +552,43 @@ public void testIsInValidSign() throws URISyntaxException, IOException, XMLEntit assertEquals("In order to validate the sign on the Logout Response, the x509cert of the IdP is required", logoutResponse.getError()); } + /** + * Tests the isValid method of LogoutResponse + * Case: Signed with deprecated method and flag enabled + * + * @throws IOException + * @throws URISyntaxException + * @throws XMLEntityException + * @throws Error + * + * @see com.onelogin.saml2.logout.LogoutResponse#isValid + */ + @Test + public void testIsInValidSignWithDeprecatedAlg() throws URISyntaxException, IOException, XMLEntityException, Error { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + settings.setStrict(false); + settings.setWantMessagesSigned(true); + + final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; + String samlResponseEncoded = "fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A"; + String relayState = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php"; + String sigAlg = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + String signature = "vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA="; + + HttpRequest httpRequest = new HttpRequest(requestURL, (String)null) + .addParameter("SAMLResponse", samlResponseEncoded) + .addParameter("RelayState", relayState) + .addParameter("SigAlg", sigAlg) + .addParameter("Signature", signature); + + LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); + assertTrue(logoutResponse.isValid()); + + settings.setRejectDeprecatedAlg(true); + LogoutResponse logoutResponse2 = new LogoutResponse(settings, httpRequest); + assertFalse(logoutResponse2.isValid()); + } + /** * Tests the isValid method of LogoutResponse * Case: No SAML Logout Response 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 61616237..b8eb5b97 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 @@ -1234,6 +1234,12 @@ public void testValidateMetadataSign() throws URISyntaxException, IOException, C assertTrue(Util.validateMetadataSign(signedMetadataDocument, null, fingerprint_sha1, null)); assertTrue(Util.validateMetadataSign(signedMetadataDocument, null, fingerprint_sha1, "SHA-1")); assertTrue(Util.validateMetadataSign(signedMetadataDocument, null, fingerprint_sha256, "SHA-256")); + + // Deprecated Alg + String signed256MetadataStr = Util.getFileAsString("data/metadata/signed_metadata_settings256.xml"); + Document signed256MetadataDocument = Util.loadXML(signed256MetadataStr); + assertFalse(Util.validateMetadataSign(signedMetadataDocument, null, fingerprint_sha1, "SHA-1", true)); + assertTrue(Util.validateMetadataSign(signed256MetadataDocument, null, fingerprint_sha1, "SHA-1", true)); } /** diff --git a/core/src/test/resources/data/metadata/signed_metadata_settings256.xml b/core/src/test/resources/data/metadata/signed_metadata_settings256.xml new file mode 100644 index 00000000..3fcb1151 --- /dev/null +++ b/core/src/test/resources/data/metadata/signed_metadata_settings256.xml @@ -0,0 +1,38 @@ + + + + + 4Sefh/fD7F51Eh+fyfx9xiko0Q0=RFlwMeIJf5SB1CV5URpjSdpa9BrtDa/TFN2kUncsQYus/xWuxucbygHhhuCz4/3k4K7cUhFk5x3KtBC0yW4cUXBYagE0qdMpYtfXLDfrQE1rT2TgACUXJh/wWxkqR6NuAbw3Fy9PnxsLKKksO8ZoJZyYl6IgcQmIVx2ii3ACKdQ= +MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo + + + + MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo + + + + + + + MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + + sp_test + SP test + http://sp.example.com + + + technical_name + technical@example.com + + + support_name + support@example.com + + \ No newline at end of file diff --git a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties index 4d705fa6..1a071318 100644 --- a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties +++ b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties @@ -85,7 +85,7 @@ onelogin.saml2.idp.x509cert = # let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512 # 'sha1' is the default value. # onelogin.saml2.idp.certfingerprint = -# onelogin.saml2.idp.certfingerprint_algorithm = sha1 +# onelogin.saml2.idp.certfingerprint_algorithm = sha256 # Security settings @@ -145,7 +145,17 @@ onelogin.saml2.security.want_xml_validation = true # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' # 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' -onelogin.saml2.security.signature_algorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1 +onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256 + +# Reject Signatures with deprecated algorithms (sha1) +onelogin.saml2.security.reject_deprecated_alg = true # Organization onelogin.saml2.organization.name = SP Java From ec3af78bd6836fc98b27bac79099cb27a817a0f5 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 20 Nov 2020 17:57:14 +0100 Subject: [PATCH 042/133] See #291 Support sending extra GET parameters on login and logout --- .../main/java/com/onelogin/saml2/Auth.java | 70 ++++++++++++++++ .../com/onelogin/saml2/test/AuthTest.java | 79 ++++++++++++++++--- 2 files changed, 140 insertions(+), 9 deletions(-) diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index aad28bd8..092f9324 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -332,9 +332,41 @@ public void setStrict(Boolean value) { public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, String nameIdValueReq) throws IOException, SettingsException { Map parameters = new HashMap(); + return login(returnTo, forceAuthn, isPassive, setNameIdPolicy, stay, + nameIdValueReq, parameters); + } + /** + * Initiates the SSO process. + * + * @param returnTo The target URL the user should be returned to after + * login (relayState). Will be a self-routed URL when + * null, or not be appended at all when an empty string + * is provided + * @param forceAuthn When true the AuthNRequest will set the + * ForceAuthn='true' + * @param isPassive When true the AuthNRequest will set the + * IsPassive='true' + * @param setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy + * @param stay True if we want to stay (returns the url string) False + * to execute redirection + * @param nameIdValueReq Indicates to the IdP the subject that should be + * authenticated + * @param parameters Use it to send extra parameters in addition to the AuthNRequest + * + * @return the SSO URL with the AuthNRequest if stay = True + * + * @throws IOException + * @throws SettingsException + */ + public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, + String nameIdValueReq, Map parameters) throws IOException, SettingsException { AuthnRequest authnRequest = new AuthnRequest(settings, forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq); + if (parameters == null) { + parameters = new HashMap(); + } + String samlRequest = authnRequest.getEncodedAuthnRequest(); parameters.put("SAMLRequest", samlRequest); @@ -468,6 +500,44 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea String nameIdNameQualifier, String nameIdSPNameQualifier) throws IOException, XMLEntityException, SettingsException { Map parameters = new HashMap(); + return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, + nameIdNameQualifier, nameIdSPNameQualifier, parameters); + } + + /** + * Initiates the SLO process. + * + * @param returnTo The target URL the user should be returned to + * after logout (relayState). Will be a self-routed + * URL when null, or not be appended at all when an + * empty string is provided + * @param nameId The NameID that will be set in the + * LogoutRequest. + * @param sessionIndex The SessionIndex (taken from the SAML Response + * in the SSO process). + * @param stay True if we want to stay (returns the url string) + * False to execute redirection + * @param nameidFormat The NameID Format that will be set in the + * LogoutRequest. + * @param nameIdNameQualifier The NameID NameQualifier that will be set in the + * LogoutRequest. + * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in + * the LogoutRequest. + * @param parameters Use it to send extra parameters in addition to the LogoutRequest + * + * @return the SLO URL with the LogoutRequest if stay = True + * + * @throws IOException + * @throws XMLEntityException + * @throws SettingsException + */ + public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, + String nameIdNameQualifier, String nameIdSPNameQualifier, Map parameters) + throws IOException, XMLEntityException, SettingsException { + + if (parameters == null) { + parameters = new HashMap(); + } LogoutRequest logoutRequest = new LogoutRequest(settings, null, nameId, sessionIndex, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier); 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 0900d755..12b62b68 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -1283,7 +1283,7 @@ public void testLogin() throws IOException, SettingsException, URISyntaxExceptio HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1311,7 +1311,7 @@ public void testLoginWithRelayState() throws IOException, SettingsException, URI HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1354,6 +1354,37 @@ public void testLoginWithoutRelayState() throws IOException, SettingsException, assertThat(urlCaptor.getValue(), not(containsString("&RelayState="))); } + /** + * Tests the login method of Auth + * Case: Login with extra parameters + * + * @throws SettingsException + * @throws IOException + * @throws URISyntaxException + * @throws Error + * + * @see com.onelogin.saml2.Auth#login + */ + @Test + public void testLoginWithExtraParameters() throws IOException, SettingsException, URISyntaxException, Error { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(request.getScheme()).thenReturn("http"); + when(request.getServerPort()).thenReturn(8080); + when(request.getServerName()).thenReturn("localhost"); + when(request.getRequestURI()).thenReturn("/initial.jsp"); + + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + settings.setAuthnRequestsSigned(false); + + Auth auth = new Auth(settings, request, response); + Map extraParameters = new HashMap(); + extraParameters.put("parameter1", "xxx"); + String target = auth.login("", false, false, false, true, null, extraParameters); + assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php?SAMLRequest=")); + assertThat(target, containsString("¶meter1=xxx")); + } + /** * Tests the login method of Auth * Case: Login with stay enabled @@ -1454,7 +1485,7 @@ public void testLoginSignedFail() throws IOException, SettingsException, URISynt HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); @@ -1483,7 +1514,7 @@ public void testLoginSigned() throws IOException, SettingsException, URISyntaxEx HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1517,7 +1548,7 @@ public void testLogout() throws IOException, SettingsException, XMLEntityExcepti HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1529,6 +1560,36 @@ public void testLogout() throws IOException, SettingsException, XMLEntityExcepti assertThat(auth.getLastRequestId(), startsWith(Util.UNIQUE_ID_PREFIX)); } + /** + * Tests the logout method of Auth + * Case: Logout with no parameters + * + * @throws IOException + * @throws SettingsException + * @throws XMLEntityException + * @throws Error + * + * @see com.onelogin.saml2.Auth#logout + */ + @Test + public void testLogoutWithExtraParameters() throws IOException, SettingsException, XMLEntityException, Error { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(request.getScheme()).thenReturn("http"); + when(request.getServerPort()).thenReturn(8080); + when(request.getServerName()).thenReturn("localhost"); + when(request.getRequestURI()).thenReturn("/initial.jsp"); + + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + settings.setLogoutRequestSigned(false); + Auth auth = new Auth(settings, request, response); + Map extraParameters = new HashMap(); + extraParameters.put("parameter1", "xxx"); + String target = auth.logout("", null, null, true, null, null, null, extraParameters); + assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php?SAMLRequest=")); + assertThat(target, containsString("¶meter1=xxx")); + } + /** * Tests the logout method of Auth * Case: Logout with RelayState @@ -1546,7 +1607,7 @@ public void testLogoutWithRelayState() throws IOException, SettingsException, XM HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1608,7 +1669,7 @@ public void testLogoutStay() throws IOException, SettingsException, XMLEntityExc HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); @@ -1642,7 +1703,7 @@ public void testLogoutSignedFail() throws IOException, SettingsException, XMLEnt HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); @@ -1671,7 +1732,7 @@ public void testLogoutSigned() throws IOException, SettingsException, XMLEntityE HttpServletResponse response = mock(HttpServletResponse.class); when(request.getScheme()).thenReturn("http"); when(request.getServerPort()).thenReturn(8080); - when(request.getServerName()).thenReturn("localhost"); + when(request.getServerName()).thenReturn("localhost"); when(request.getRequestURI()).thenReturn("/initial.jsp"); Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); From 226795cd6405877e743e19beb0178586b853e0a0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 20 Nov 2020 18:02:19 +0100 Subject: [PATCH 043/133] Update Readme --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e91b0eda..75bfc34a 100644 --- a/README.md +++ b/README.md @@ -426,12 +426,13 @@ We can set a 'returnTo' url parameter to the login function and that will be con String targetUrl = 'https://example.com'; auth.login(returnTo=targetUrl) ``` -The login method can receive 5 more optional parameters: +The login method can receive 6 more optional parameters: - *forceAuthn* When true the AuthNRequest will have the 'ForceAuthn' attribute set to 'true' - *isPassive* When true the AuthNRequest will have the 'Ispassive' attribute set to 'true' - *setNameIdPolicy* When true the AuthNRequest will set a nameIdPolicy element. - *stay* Set to true to stay (returns the url string), otherwise set to false to execute a redirection to that url (IdP SSO URL) - *nameIdValueReq* Indicates to the IdP the subject that should be authenticated +- *parameters* Use it to send extra parameters in addition to the AuthNRequest By default, the login method initiates a redirect to the SAML Identity Provider. You can use the *stay* parameter, to prevent that, and execute the redirection manually. We need to use that if a match on the future SAMLResponse ID and the AuthNRequest ID to be sent is required. That AuthNRequest ID must be extracted and stored for future validation, so we can't execute the redirection on the login. Instead, set *stay* to true, then get that ID by ``` @@ -598,11 +599,15 @@ String targetUrl = 'https://example.com'; auth.logout(returnTo=targetUrl) ``` -Also there are 3 optional parameters that can be set: +Also there are 7 optional parameters that can be set: - nameId. That will be used to build the LogoutRequest. If not name_id parameter is set and the auth object processed a SAML Response with a NameId, then this NameId will be used. - sessionIndex. Identifies the session of the user. If a match on the LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored for future validation, we can get that ID by - stay. True if we want to stay (returns the url string) False to execute a redirection to that url (IdP SLS URL) +- nameidFormat. The NameID Format that will be set in the LogoutRequest +- nameIdNameQualifier. The NameID NameQualifier that will be set in the LogoutRequest +- nameIdSPNameQualifier. The NameID SP Name Qualifier that will be set in the LogoutRequest +- parameters. Use it to send extra parameters in addition to the LogoutRequest By default the logout method initiates a redirect to the SAML Identity Provider. You can use the stay parameter, to prevent that, and execute the redirection manually. We need to use that if a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must be extracted and stored for future validation so we can't execute the redirection on the logout, instead set stay to true, then get that ID by From b45a2d2b6fd60aecdd2ddd0e53df2f65c4404d68 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 20 Nov 2020 20:11:37 +0100 Subject: [PATCH 044/133] See #289 Fix SettingsBuilder build method in order to fix injectIntoSettings method --- .../saml2/settings/SettingsBuilder.java | 86 ++++++++++++------- .../test/settings/IdPMetadataParserTest.java | 55 +++++++++++- 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 802ed322..78594445 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -239,23 +239,36 @@ public Saml2Settings build(Saml2Settings saml2Setting) { this.saml2Setting = saml2Setting; Boolean strict = loadBooleanProperty(STRICT_PROPERTY_KEY); - if (strict != null) + if (strict != null) { saml2Setting.setStrict(strict); + } Boolean debug = loadBooleanProperty(DEBUG_PROPERTY_KEY); - if (debug != null) + if (debug != null) { saml2Setting.setDebug(debug); + } this.loadSpSetting(); this.loadIdpSetting(); this.loadSecuritySetting(); this.loadCompressSetting(); - saml2Setting.setContacts(loadContacts()); + List contacts = this.loadContacts(); + if (!contacts.isEmpty()) { + saml2Setting.setContacts(loadContacts()); + } - saml2Setting.setOrganization(loadOrganization()); + Organization org = this.loadOrganization(); + if (org != null) { + saml2Setting.setOrganization(org); + } - saml2Setting.setUniqueIDPrefix(loadUniqueIDPrefix()); + String uniqueIdPrefix = loadUniqueIDPrefix(); + if (StringUtils.isNotEmpty(uniqueIdPrefix)) { + saml2Setting.setUniqueIDPrefix(uniqueIdPrefix); + } else if (saml2Setting.getUniqueIDPrefix() == null){ + saml2Setting.setUniqueIDPrefix(Util.UNIQUE_ID_PREFIX); + } return saml2Setting; } @@ -265,32 +278,39 @@ public Saml2Settings build(Saml2Settings saml2Setting) { */ private void loadIdpSetting() { String idpEntityID = loadStringProperty(IDP_ENTITYID_PROPERTY_KEY); - if (idpEntityID != null) + if (idpEntityID != null) { saml2Setting.setIdpEntityId(idpEntityID); + } URL idpSingleSignOnServiceUrl = loadURLProperty(IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY); - if (idpSingleSignOnServiceUrl != null) + if (idpSingleSignOnServiceUrl != null) { saml2Setting.setIdpSingleSignOnServiceUrl(idpSingleSignOnServiceUrl); + } String idpSingleSignOnServiceBinding = loadStringProperty(IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY); - if (idpSingleSignOnServiceBinding != null) + if (idpSingleSignOnServiceBinding != null) { saml2Setting.setIdpSingleSignOnServiceBinding(idpSingleSignOnServiceBinding); + } URL idpSingleLogoutServiceUrl = loadURLProperty(IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY); - if (idpSingleLogoutServiceUrl != null) + if (idpSingleLogoutServiceUrl != null) { saml2Setting.setIdpSingleLogoutServiceUrl(idpSingleLogoutServiceUrl); + } URL idpSingleLogoutServiceResponseUrl = loadURLProperty(IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY); - if (idpSingleLogoutServiceResponseUrl != null) + if (idpSingleLogoutServiceResponseUrl != null) { saml2Setting.setIdpSingleLogoutServiceResponseUrl(idpSingleLogoutServiceResponseUrl); + } String idpSingleLogoutServiceBinding = loadStringProperty(IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY); - if (idpSingleLogoutServiceBinding != null) + if (idpSingleLogoutServiceBinding != null) { saml2Setting.setIdpSingleLogoutServiceBinding(idpSingleLogoutServiceBinding); + } List idpX509certMulti = loadCertificateListFromProp(IDP_X509CERTMULTI_PROPERTY_KEY); - if (idpX509certMulti != null) + if (idpX509certMulti != null) { saml2Setting.setIdpx509certMulti(idpX509certMulti); + } X509Certificate idpX509cert = loadCertificateFromProp(IDP_X509CERT_PROPERTY_KEY); if (idpX509cert != null) { @@ -298,12 +318,14 @@ private void loadIdpSetting() { } String idpCertFingerprint = loadStringProperty(CERTFINGERPRINT_PROPERTY_KEY); - if (idpCertFingerprint != null) + if (idpCertFingerprint != null) { saml2Setting.setIdpCertFingerprint(idpCertFingerprint); + } String idpCertFingerprintAlgorithm = loadStringProperty(CERTFINGERPRINT_ALGORITHM_PROPERTY_KEY); - if (idpCertFingerprintAlgorithm != null && !idpCertFingerprintAlgorithm.isEmpty()) + if (idpCertFingerprintAlgorithm != null && !idpCertFingerprintAlgorithm.isEmpty()) { saml2Setting.setIdpCertFingerprintAlgorithm(idpCertFingerprintAlgorithm); + } } /** @@ -376,8 +398,9 @@ private void loadSecuritySetting() { } Boolean allowRepeatAttributeName = loadBooleanProperty(SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY); - if (allowRepeatAttributeName != null) + if (allowRepeatAttributeName != null) { saml2Setting.setAllowRepeatAttributeName(allowRepeatAttributeName); + } } /** @@ -443,11 +466,7 @@ private List loadContacts() { */ private String loadUniqueIDPrefix() { String uniqueIDPrefix = loadStringProperty(UNIQUE_ID_PREFIX_PROPERTY_KEY); - if (StringUtils.isNotEmpty(uniqueIDPrefix)) { - return uniqueIDPrefix; - } else { - return Util.UNIQUE_ID_PREFIX; - } + return uniqueIDPrefix; } /** @@ -455,28 +474,34 @@ private String loadUniqueIDPrefix() { */ private void loadSpSetting() { String spEntityID = loadStringProperty(SP_ENTITYID_PROPERTY_KEY); - if (spEntityID != null) + if (spEntityID != null) { saml2Setting.setSpEntityId(spEntityID); + } URL assertionConsumerServiceUrl = loadURLProperty(SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY); - if (assertionConsumerServiceUrl != null) + if (assertionConsumerServiceUrl != null) { saml2Setting.setSpAssertionConsumerServiceUrl(assertionConsumerServiceUrl); + } String spAssertionConsumerServiceBinding = loadStringProperty(SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY); - if (spAssertionConsumerServiceBinding != null) + if (spAssertionConsumerServiceBinding != null) { saml2Setting.setSpAssertionConsumerServiceBinding(spAssertionConsumerServiceBinding); + } URL spSingleLogoutServiceUrl = loadURLProperty(SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY); - if (spSingleLogoutServiceUrl != null) + if (spSingleLogoutServiceUrl != null) { saml2Setting.setSpSingleLogoutServiceUrl(spSingleLogoutServiceUrl); + } String spSingleLogoutServiceBinding = loadStringProperty(SP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY); - if (spSingleLogoutServiceBinding != null) + if (spSingleLogoutServiceBinding != null) { saml2Setting.setSpSingleLogoutServiceBinding(spSingleLogoutServiceBinding); + } String spNameIDFormat = loadStringProperty(SP_NAMEIDFORMAT_PROPERTY_KEY); - if (spNameIDFormat != null && !spNameIDFormat.isEmpty()) + if (spNameIDFormat != null && !spNameIDFormat.isEmpty()) { saml2Setting.setSpNameIDFormat(spNameIDFormat); + } boolean keyStoreEnabled = this.samlData.get(KEYSTORE_KEY) != null && this.samlData.get(KEYSTORE_ALIAS) != null && this.samlData.get(KEYSTORE_KEY_PASSWORD) != null; @@ -496,14 +521,17 @@ private void loadSpSetting() { spPrivateKey = loadPrivateKeyFromProp(SP_PRIVATEKEY_PROPERTY_KEY); } - if (spX509cert != null) + if (spX509cert != null) { saml2Setting.setSpX509cert(spX509cert); - if (spPrivateKey != null) + } + if (spPrivateKey != null) { saml2Setting.setSpPrivateKey(spPrivateKey); + } X509Certificate spX509certNew = loadCertificateFromProp(SP_X509CERTNEW_PROPERTY_KEY); - if (spX509certNew != null) + if (spX509certNew != null) { saml2Setting.setSpX509certNew(spX509certNew); + } } /** diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index 34f7203f..6ff98b24 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -5,12 +5,14 @@ import static org.junit.Assert.assertNull; import java.net.URL; +import java.util.List; import java.util.Map; import org.junit.Test; import org.w3c.dom.Document; import org.xml.sax.InputSource; +import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.settings.IdPMetadataParser; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -211,7 +213,7 @@ public void testParseMultiSameSigningAndEncryptCert() throws Exception { @Test public void testInjectIntoSettings() throws Exception { - Saml2Settings setting = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Saml2Settings setting = new SettingsBuilder().fromFile("config/config.all.properties").build(); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -220,6 +222,14 @@ public void testInjectIntoSettings() throws Exception { assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting.getIdpx509cert()); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); + assertEquals(2, setting.getContacts().size()); + assertEquals("technical@example.com", setting.getContacts().get(0).getEmailAddress()); + assertEquals("support@example.com", setting.getContacts().get(1).getEmailAddress()); + assertEquals("SP Java", setting.getOrganization().getOrgName()); + assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/FederationMetadata.xml"); setting = IdPMetadataParser.injectIntoSettings(setting, idpInfo); @@ -228,11 +238,52 @@ public void testInjectIntoSettings() throws Exception { assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleLogoutServiceUrl().toString()); assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); assertEquals(setting.getIdpx509cert(), Util.loadCert( "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); assertEquals(setting.getIdpx509certMulti().get(0), Util.loadCert( "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); + assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); + assertEquals(2, setting.getContacts().size()); + assertEquals("technical@example.com", setting.getContacts().get(0).getEmailAddress()); + assertEquals("support@example.com", setting.getContacts().get(1).getEmailAddress()); + assertEquals("SP Java", setting.getOrganization().getOrgName()); + assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); + + Saml2Settings setting2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); + assertEquals("http://idp.example.com/", setting2.getIdpEntityId()); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting2.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting2.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting2.getIdpx509cert()); + assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); + assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); + assertEquals(0, setting2.getContacts().size()); + assertNull(setting2.getOrganization()); + assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); + + setting2 = IdPMetadataParser.injectIntoSettings(setting2, idpInfo); + assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting2.getIdpEntityId()); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(setting2.getIdpx509cert(), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); + assertEquals(setting2.getIdpx509certMulti().get(0), Util.loadCert( + "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); + assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); + assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); + assertEquals(0, setting2.getContacts().size()); + assertNull(setting2.getOrganization()); + assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); } } \ No newline at end of file From e6d0501ba967b93832632ee4e963a0017e20c790 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Mon, 23 Nov 2020 11:36:57 +0100 Subject: [PATCH 045/133] Improve deprecated-alg code --- .../onelogin/saml2/logout/LogoutRequest.java | 11 +++----- .../onelogin/saml2/logout/LogoutResponse.java | 11 +++----- .../java/com/onelogin/saml2/util/Util.java | 25 +++++++++++++------ 3 files changed, 24 insertions(+), 23 deletions(-) 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 9ae8499c..9caa9b39 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -457,14 +457,9 @@ public Boolean isValid() throws Exception { signAlg = Constants.RSA_SHA1; } - if (signAlg.equals(Constants.RSA_SHA1)) { - Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); - if (rejectDeprecatedAlg) { - LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); - return false; - } else { - LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); - } + Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); + if (Util.mustRejectDeprecatedSignatureAlgo(signAlg, rejectDeprecatedAlg)) { + return false; } String relayState = request.getEncodedParameter("RelayState"); 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 9cf14e7c..e943d826 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -258,14 +258,9 @@ public Boolean isValid(String requestId) { signAlg = Constants.RSA_SHA1; } - if (signAlg.equals(Constants.RSA_SHA1)) { - Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); - if (rejectDeprecatedAlg) { - LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); - return false; - } else { - LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); - } + Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg(); + if (Util.mustRejectDeprecatedSignatureAlgo(signAlg, rejectDeprecatedAlg)) { + return false; } String signedQuery = "SAMLResponse=" + request.getEncodedParameter("SAMLResponse"); 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 6b72894b..1c32e5e4 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -27,6 +27,7 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; @@ -116,6 +117,8 @@ public final class Util { /** Indicates if JAXP 1.5 support has been detected. */ private static boolean JAXP_15_SUPPORTED = isJaxp15Supported(); + private static final Set DEPRECATED_ALGOS = new HashSet<>(Arrays.asList(Constants.RSA_SHA1, Constants.DSA_SHA1)); + static { System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true"); org.apache.xml.security.Init.init(); @@ -1093,13 +1096,8 @@ private static Map getSignatureData(Node signNode, String alg, Bo throw new Exception(sigMethodAlg + " is not a valid supported algorithm"); } - if (sigMethodAlg.equals(Constants.RSA_SHA1)) { - if (rejectDeprecatedAlg) { - LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it"); - return signatureData; - } else { - LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg"); - } + if (Util.mustRejectDeprecatedSignatureAlgo(sigMethodAlg, rejectDeprecatedAlg)) { + return signatureData; } signatureData.put("signature", signature); @@ -1122,6 +1120,19 @@ private static Map getSignatureData(Node signNode, String alg, Bo return signatureData; } + public static Boolean mustRejectDeprecatedSignatureAlgo(String signAlg, Boolean rejectDeprecatedAlg) { + if (DEPRECATED_ALGOS.contains(signAlg)) { + String errorMsg = "Found a deprecated algorithm "+ signAlg +" related to the Signature element,"; + if (rejectDeprecatedAlg) { + LOGGER.error(errorMsg + " rejecting it"); + return true; + } else { + LOGGER.info(errorMsg + " consider requesting a more robust algorithm"); + } + } + return false; + } + /** * Validate signature of the Node. * From 14b7956c50a7dd96b77cf63eaa5571436fd07455 Mon Sep 17 00:00:00 2001 From: Martin Samuelsson Date: Tue, 16 Feb 2021 00:11:34 +0100 Subject: [PATCH 046/133] Add support for parsing SingleLogoutService ResponseLocation in IdPMetadataParser --- .../saml2/settings/IdPMetadataParser.java | 4 ++ .../test/settings/IdPMetadataParserTest.java | 11 ++++++ .../idp/metadata_slo_responselocation.xml | 38 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 core/src/test/resources/data/metadata/idp/metadata_slo_responselocation.xml diff --git a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java index 9491c001..197d7476 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java +++ b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java @@ -92,6 +92,10 @@ public static Map parseXML(Document xmlDocument, String entityId if (sloNodes.getLength() > 0) { metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Location").getNodeValue()); metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Binding").getNodeValue()); + Node responseLocationNode = sloNodes.item(0).getAttributes().getNamedItem("ResponseLocation"); + if (responseLocationNode != null) { + metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY, responseLocationNode.getNodeValue()); + } } NodeList keyDescriptorCertSigningNodes = Util.query(xmlDocument, "./md:KeyDescriptor[not(contains(@use, \"encryption\"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate", diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index 6ff98b24..7e45aace 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -210,6 +210,17 @@ public void testParseMultiSameSigningAndEncryptCert() throws Exception { assertNull(idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo2.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); } + + @Test + public void testParseSeparateSingleLogoutServiceResponseLocation() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata_slo_responselocation.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sloresp", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + } @Test public void testInjectIntoSettings() throws Exception { diff --git a/core/src/test/resources/data/metadata/idp/metadata_slo_responselocation.xml b/core/src/test/resources/data/metadata/idp/metadata_slo_responselocation.xml new file mode 100644 index 00000000..4fdf6690 --- /dev/null +++ b/core/src/test/resources/data/metadata/idp/metadata_slo_responselocation.xml @@ -0,0 +1,38 @@ + + + + + + + MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF +BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj +aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW +T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy +MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz +Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV +BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo +3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw +tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx +VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5 +L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t +1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/ +BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB +pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD +VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL +DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC +FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B +AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM +GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c +hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB +vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37 +MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ +WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw== + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + \ No newline at end of file From 44587fbb0e98ec56be0864ea75fe5cd17a0422fa Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 25 Feb 2021 12:53:19 +0100 Subject: [PATCH 047/133] Fix description of the SSO binding configuration parameter in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d02efbe..14d82177 100644 --- a/README.md +++ b/README.md @@ -249,8 +249,8 @@ onelogin.saml2.idp.entityid = # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect From 06212a2b5988c8ffd9b1535d934209a8d6389564 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 5 Mar 2021 17:13:36 +0100 Subject: [PATCH 048/133] Make constants real constants --- .../com/onelogin/saml2/util/Constants.java | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/util/Constants.java b/core/src/main/java/com/onelogin/saml2/util/Constants.java index cb22b2f9..dd3037a7 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Constants.java +++ b/core/src/main/java/com/onelogin/saml2/util/Constants.java @@ -9,114 +9,114 @@ public final class Constants { /** * Value added to the current time in time condition validations. */ - public static Integer ALOWED_CLOCK_DRIFT = 180; // 3 min in seconds + public static final Integer ALOWED_CLOCK_DRIFT = 180; // 3 min in seconds // NameID Formats - public static String NAMEID_EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; - public static String NAMEID_X509_SUBJECT_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; - public static String NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; - public static String NAMEID_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"; - public static String NAMEID_KERBEROS = "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"; - public static String NAMEID_ENTITY = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"; - public static String NAMEID_TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"; - public static String NAMEID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; - public static String NAMEID_ENCRYPTED = "urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"; + public static final String NAMEID_EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; + public static final String NAMEID_X509_SUBJECT_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; + public static final String NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; + public static final String NAMEID_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"; + public static final String NAMEID_KERBEROS = "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"; + public static final String NAMEID_ENTITY = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"; + public static final String NAMEID_TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"; + public static final String NAMEID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; + public static final String NAMEID_ENCRYPTED = "urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"; // Attribute Name Formats - public static String ATTRNAME_FORMAT_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"; - public static String ATTRNAME_FORMAT_URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"; - public static String ATTRNAME_FORMAT_BASIC = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"; + public static final String ATTRNAME_FORMAT_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"; + public static final String ATTRNAME_FORMAT_URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"; + public static final String ATTRNAME_FORMAT_BASIC = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"; // Namespaces - public static String NS_SAML = "urn:oasis:names:tc:SAML:2.0:assertion"; - public static String NS_SAMLP = "urn:oasis:names:tc:SAML:2.0:protocol"; - public static String NS_SOAP = "http://schemas.xmlsoap.org/soap/envelope/"; - public static String NS_MD = "urn:oasis:names:tc:SAML:2.0:metadata"; - public static String NS_XS = "http://www.w3.org/2001/XMLSchema"; - public static String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"; - public static String NS_XENC = "http://www.w3.org/2001/04/xmlenc#"; - public static String NS_DS = "http://www.w3.org/2000/09/xmldsig#"; + public static final String NS_SAML = "urn:oasis:names:tc:SAML:2.0:assertion"; + public static final String NS_SAMLP = "urn:oasis:names:tc:SAML:2.0:protocol"; + public static final String NS_SOAP = "http://schemas.xmlsoap.org/soap/envelope/"; + public static final String NS_MD = "urn:oasis:names:tc:SAML:2.0:metadata"; + public static final String NS_XS = "http://www.w3.org/2001/XMLSchema"; + public static final String NS_XSI = "http://www.w3.org/2001/XMLSchema-instance"; + public static final String NS_XENC = "http://www.w3.org/2001/04/xmlenc#"; + public static final String NS_DS = "http://www.w3.org/2000/09/xmldsig#"; // Bindings - public static String BINDING_HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; - public static String BINDING_HTTP_REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"; - public static String BINDING_HTTP_ARTIFACT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"; - public static String BINDING_SOAP = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP"; - public static String BINDING_DEFLATE = "urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE"; + public static final String BINDING_HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; + public static final String BINDING_HTTP_REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"; + public static final String BINDING_HTTP_ARTIFACT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"; + public static final String BINDING_SOAP = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP"; + public static final String BINDING_DEFLATE = "urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE"; // Auth Context Class - public static String AC_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"; - public static String AC_PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"; - public static String AC_X509 = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509"; - public static String AC_SMARTCARD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard"; - public static String AC_KERBEROS = "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos"; + public static final String AC_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"; + public static final String AC_PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"; + public static final String AC_X509 = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509"; + public static final String AC_SMARTCARD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard"; + public static final String AC_KERBEROS = "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos"; // Subject Confirmation - public static String CM_BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"; - public static String CM_HOLDER_KEY = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"; - public static String CM_SENDER_VOUCHES = "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"; + public static final String CM_BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"; + public static final String CM_HOLDER_KEY = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"; + public static final String CM_SENDER_VOUCHES = "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"; // Status Codes - public static String STATUS_SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"; - public static String STATUS_REQUESTER = "urn:oasis:names:tc:SAML:2.0:status:Requester"; - public static String STATUS_RESPONDER = "urn:oasis:names:tc:SAML:2.0:status:Responder"; - public static String STATUS_VERSION_MISMATCH = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"; + public static final String STATUS_SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"; + public static final String STATUS_REQUESTER = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + public static final String STATUS_RESPONDER = "urn:oasis:names:tc:SAML:2.0:status:Responder"; + public static final String STATUS_VERSION_MISMATCH = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"; // Status Second-level Codes - public static String STATUS_AUTHNFAILED = "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"; - public static String STATUS_INVALID_ATTRNAME_OR_VALUE = "urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue"; - public static String STATUS_INVALID_NAMEIDPOLICY = "urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy"; - public static String STATUS_NO_AUTHNCONTEXT = "urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext"; - public static String STATUS_NO_AVAILABLE_IDP = "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"; - public static String STATUS_NO_PASSIVE = "urn:oasis:names:tc:SAML:2.0:status:NoPassive"; - public static String STATUS_NO_SUPPORTED_IDP = "urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP"; - public static String STATUS_PARTIAL_LOGOUT = "urn:oasis:names:tc:SAML:2.0:status:PartialLogout"; - public static String STATUS_PROXY_COUNT_EXCEEDED = "urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded"; - public static String STATUS_REQUEST_DENIED = "urn:oasis:names:tc:SAML:2.0:status:RequestDenied"; - public static String STATUS_REQUEST_UNSUPPORTED = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"; - public static String STATUS_REQUEST_VERSION_DEPRECATED = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"; - public static String STATUS_REQUEST_VERSION_TOO_HIGH = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"; - public static String STATUS_REQUEST_VERSION_TOO_LOW = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow"; - public static String STATUS_RESOURCE_NOT_RECOGNIZED = "urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized"; - public static String STATUS_TOO_MANY_RESPONSES = "urn:oasis:names:tc:SAML:2.0:status:TooManyResponses"; - public static String STATUS_UNKNOWN_ATTR_PROFILE = "urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile"; - public static String STATUS_UNKNOWN_PRINCIPAL = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"; - public static String STATUS_UNSUPPORTED_BINDING = "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding"; + public static final String STATUS_AUTHNFAILED = "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"; + public static final String STATUS_INVALID_ATTRNAME_OR_VALUE = "urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue"; + public static final String STATUS_INVALID_NAMEIDPOLICY = "urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy"; + public static final String STATUS_NO_AUTHNCONTEXT = "urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext"; + public static final String STATUS_NO_AVAILABLE_IDP = "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"; + public static final String STATUS_NO_PASSIVE = "urn:oasis:names:tc:SAML:2.0:status:NoPassive"; + public static final String STATUS_NO_SUPPORTED_IDP = "urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP"; + public static final String STATUS_PARTIAL_LOGOUT = "urn:oasis:names:tc:SAML:2.0:status:PartialLogout"; + public static final String STATUS_PROXY_COUNT_EXCEEDED = "urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded"; + public static final String STATUS_REQUEST_DENIED = "urn:oasis:names:tc:SAML:2.0:status:RequestDenied"; + public static final String STATUS_REQUEST_UNSUPPORTED = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"; + public static final String STATUS_REQUEST_VERSION_DEPRECATED = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"; + public static final String STATUS_REQUEST_VERSION_TOO_HIGH = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"; + public static final String STATUS_REQUEST_VERSION_TOO_LOW = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow"; + public static final String STATUS_RESOURCE_NOT_RECOGNIZED = "urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized"; + public static final String STATUS_TOO_MANY_RESPONSES = "urn:oasis:names:tc:SAML:2.0:status:TooManyResponses"; + public static final String STATUS_UNKNOWN_ATTR_PROFILE = "urn:oasis:names:tc:SAML:2.0:status:UnknownAttrProfile"; + public static final String STATUS_UNKNOWN_PRINCIPAL = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"; + public static final String STATUS_UNSUPPORTED_BINDING = "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding"; // Canonization - public static String C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; - public static String C14N_WC = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; - public static String C14N11 = "http://www.w3.org/2006/12/xml-c14n11"; - public static String C14N11_WC = "http://www.w3.org/2006/12/xml-c14n11#WithComments"; - public static String C14NEXC = "http://www.w3.org/2001/10/xml-exc-c14n#"; - public static String C14NEXC_WC = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; + public static final String C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; + public static final String C14N_WC = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; + public static final String C14N11 = "http://www.w3.org/2006/12/xml-c14n11"; + public static final String C14N11_WC = "http://www.w3.org/2006/12/xml-c14n11#WithComments"; + public static final String C14NEXC = "http://www.w3.org/2001/10/xml-exc-c14n#"; + public static final String C14NEXC_WC = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; // Sign & Crypt // https://www.w3.org/TR/xmlenc-core/#sec-Alg-MessageDigest // https://www.w3.org/TR/xmlsec-algorithms/#signature-method-uris // https://tools.ietf.org/html/rfc6931 - public static String SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"; - public static String SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"; - public static String SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"; - public static String SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"; + public static final String SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"; + public static final String SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"; + public static final String SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"; + public static final String SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512"; - public static String DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; - public static String RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; - public static String RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; - public static String RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"; - public static String RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; + public static final String DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; + public static final String RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + public static final String RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + public static final String RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"; + public static final String RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; - public static String TRIPLEDES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; - public static String AES128_CBC = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; - public static String AES192_CBC = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; - public static String AES256_CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; - public static String A128KW = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; - public static String A192KW = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; - public static String A256KW = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; - public static String RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; - public static String RSA_OAEP_MGF1P = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; + public static final String TRIPLEDES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; + public static final String AES128_CBC = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; + public static final String AES192_CBC = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; + public static final String AES256_CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; + public static final String A128KW = "http://www.w3.org/2001/04/xmlenc#kw-aes128"; + public static final String A192KW = "http://www.w3.org/2001/04/xmlenc#kw-aes192"; + public static final String A256KW = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; + public static final String RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; + public static final String RSA_OAEP_MGF1P = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; - public static String ENVSIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; + public static final String ENVSIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; private Constants() { //not called From 61a72cd951664ef13ee78950c4462aada2dd57e0 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 30 Mar 2021 17:34:58 +0200 Subject: [PATCH 049/133] Fix description of the SSO binding configuration parameter in tests This completes #304. --- core/src/test/resources/config/config.adfs.properties | 4 ++-- core/src/test/resources/config/config.all.properties | 4 ++-- .../resources/config/config.allowduplicatednames.properties | 4 ++-- core/src/test/resources/config/config.certfile.properties | 4 ++-- core/src/test/resources/config/config.certstring.properties | 4 ++-- core/src/test/resources/config/config.different.properties | 4 ++-- .../test/resources/config/config.invalidcontacts.properties | 4 ++-- .../resources/config/config.invalidspcertstring.properties | 4 ++-- .../resources/config/config.knownIdpPrivateKey.properties | 4 ++-- core/src/test/resources/config/config.my.properties | 4 ++-- .../test/resources/config/config.mywithmulticert.properties | 4 ++-- .../src/test/resources/config/config.mywithnocert.properties | 4 ++-- core/src/test/resources/config/config.newattack.properties | 4 ++-- core/src/test/resources/config/config.newattack2.properties | 4 ++-- core/src/test/resources/config/config.samecerts.properties | 4 ++-- .../test/resources/config/config.somevaluesempty.properties | 4 ++-- .../src/main/resources/onelogin.saml.properties | 5 +++-- 17 files changed, 35 insertions(+), 34 deletions(-) diff --git a/core/src/test/resources/config/config.adfs.properties b/core/src/test/resources/config/config.adfs.properties index 96540e35..8bd0072c 100644 --- a/core/src/test/resources/config/config.adfs.properties +++ b/core/src/test/resources/config/config.adfs.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://adfs.vc.example.com/adfs/services/trust # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://adfs.vc.example.com/adfs/ls -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index 8e2c85ec..2e879d5b 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -50,8 +50,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.allowduplicatednames.properties b/core/src/test/resources/config/config.allowduplicatednames.properties index 8984f14e..fb5af81c 100644 --- a/core/src/test/resources/config/config.allowduplicatednames.properties +++ b/core/src/test/resources/config/config.allowduplicatednames.properties @@ -49,8 +49,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.certfile.properties b/core/src/test/resources/config/config.certfile.properties index adfaaa76..97b63c38 100644 --- a/core/src/test/resources/config/config.certfile.properties +++ b/core/src/test/resources/config/config.certfile.properties @@ -42,8 +42,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.certstring.properties b/core/src/test/resources/config/config.certstring.properties index 85d8b6a9..7bb65492 100644 --- a/core/src/test/resources/config/config.certstring.properties +++ b/core/src/test/resources/config/config.certstring.properties @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.different.properties b/core/src/test/resources/config/config.different.properties index d8441d3f..84c392c9 100644 --- a/core/src/test/resources/config/config.different.properties +++ b/core/src/test/resources/config/config.different.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.invalidcontacts.properties b/core/src/test/resources/config/config.invalidcontacts.properties index be02fd34..66aec8ce 100644 --- a/core/src/test/resources/config/config.invalidcontacts.properties +++ b/core/src/test/resources/config/config.invalidcontacts.properties @@ -44,8 +44,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.invalidspcertstring.properties b/core/src/test/resources/config/config.invalidspcertstring.properties index 45a37d91..1974e800 100644 --- a/core/src/test/resources/config/config.invalidspcertstring.properties +++ b/core/src/test/resources/config/config.invalidspcertstring.properties @@ -44,8 +44,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.knownIdpPrivateKey.properties b/core/src/test/resources/config/config.knownIdpPrivateKey.properties index e4a43f9f..f17fdce9 100644 --- a/core/src/test/resources/config/config.knownIdpPrivateKey.properties +++ b/core/src/test/resources/config/config.knownIdpPrivateKey.properties @@ -55,8 +55,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.my.properties b/core/src/test/resources/config/config.my.properties index c86a2915..b4a43464 100644 --- a/core/src/test/resources/config/config.my.properties +++ b/core/src/test/resources/config/config.my.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.mywithmulticert.properties b/core/src/test/resources/config/config.mywithmulticert.properties index 20a5c88d..8218a617 100644 --- a/core/src/test/resources/config/config.mywithmulticert.properties +++ b/core/src/test/resources/config/config.mywithmulticert.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.mywithnocert.properties b/core/src/test/resources/config/config.mywithnocert.properties index 076de76f..d737de60 100644 --- a/core/src/test/resources/config/config.mywithnocert.properties +++ b/core/src/test/resources/config/config.mywithnocert.properties @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.newattack.properties b/core/src/test/resources/config/config.newattack.properties index fb916a8b..3255de68 100644 --- a/core/src/test/resources/config/config.newattack.properties +++ b/core/src/test/resources/config/config.newattack.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.newattack2.properties b/core/src/test/resources/config/config.newattack2.properties index 175407ff..80a5d8f6 100644 --- a/core/src/test/resources/config/config.newattack2.properties +++ b/core/src/test/resources/config/config.newattack2.properties @@ -45,8 +45,8 @@ onelogin.saml2.idp.entityid = idp.example.com # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/saml/sso -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.samecerts.properties b/core/src/test/resources/config/config.samecerts.properties index fcd91490..9d94a4d3 100644 --- a/core/src/test/resources/config/config.samecerts.properties +++ b/core/src/test/resources/config/config.samecerts.properties @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/core/src/test/resources/config/config.somevaluesempty.properties b/core/src/test/resources/config/config.somevaluesempty.properties index ca5897f9..60d87e8f 100644 --- a/core/src/test/resources/config/config.somevaluesempty.properties +++ b/core/src/test/resources/config/config.somevaluesempty.properties @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect diff --git a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties index 1a071318..ce813fd3 100644 --- a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties +++ b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties @@ -55,8 +55,9 @@ onelogin.saml2.idp.entityid = # SSO endpoint info of the IdP. (Authentication Request protocol) # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = -# SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the + +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect From 874b410d6056b180a46b6f8980b23995eaac0ce6 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 31 Mar 2021 18:14:40 +0200 Subject: [PATCH 050/133] Properly escape text to produce valid XML The generation methods for metadata, AuthnRequest, LogoutRequest and LogoutResponse XML documents and messages have been changed so that any text that could be specified by the user is properly escaped in order to produce a valid XML output: characters like &, ", < and > are then replaced with the corresponding XML entities (& ", <, >). The commons-lang StringEscapeUtils class has been used, although being deprecated: the deprecation warning suggests to switch to an equivalent class of commons-text, which java-saml currently does not depend on. I leave the decision to possibly switch to it for future evaluation: the escaping logic was indeed implemented in the Util class, so any implementation change of it just needs to change Util.toXml(String) method. Closes #309 and #305. --- .../onelogin/saml2/authn/AuthnRequest.java | 20 +- .../onelogin/saml2/logout/LogoutRequest.java | 8 +- .../onelogin/saml2/logout/LogoutResponse.java | 10 +- .../com/onelogin/saml2/settings/Metadata.java | 46 +++-- .../java/com/onelogin/saml2/util/Util.java | 16 +- .../saml2/test/authn/AuthnRequestTest.java | 127 ++++++++++++ .../saml2/test/logout/LogoutRequestTest.java | 79 +++++++- .../saml2/test/logout/LogoutResponseTest.java | 31 ++- .../saml2/test/settings/MetadataTest.java | 188 +++++++++++++++++- .../config/config.all_specialchars.properties | 155 +++++++++++++++ .../config/config.min_specialchars.properties | 26 +++ 11 files changed, 657 insertions(+), 49 deletions(-) create mode 100644 core/src/test/resources/config/config.all_specialchars.properties create mode 100644 core/src/test/resources/config/config.min_specialchars.properties diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index ba3ea21f..28f9d2ce 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -186,7 +186,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { String destinationStr = ""; URL sso = settings.getIdpSingleSignOnServiceUrl(); if (sso != null) { - destinationStr = " Destination=\"" + sso.toString() + "\""; + destinationStr = " Destination=\"" + Util.toXml(sso.toString()) + "\""; } valueMap.put("destinationStr", destinationStr); @@ -194,7 +194,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { if (nameIdValueReq != null && !nameIdValueReq.isEmpty()) { String nameIDFormat = settings.getSpNameIDFormat(); subjectStr = ""; - subjectStr += "" + nameIdValueReq + ""; + subjectStr += "" + Util.toXml(nameIdValueReq) + ""; subjectStr += ""; subjectStr += ""; } @@ -206,7 +206,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { if (settings.getWantNameIdEncrypted()) { nameIDPolicyFormat = Constants.NAMEID_ENCRYPTED; } - nameIDPolicyStr = ""; + nameIDPolicyStr = ""; } valueMap.put("nameIDPolicyStr", nameIDPolicyStr); @@ -215,25 +215,25 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { if (organization != null) { String displayName = organization.getOrgDisplayName(); if (!displayName.isEmpty()) { - providerStr = " ProviderName=\""+ displayName + "\""; + providerStr = " ProviderName=\""+ Util.toXml(displayName) + "\""; } } valueMap.put("providerStr", providerStr); String issueInstantString = Util.formatDateTime(issueInstant.getTimeInMillis()); valueMap.put("issueInstant", issueInstantString); - valueMap.put("id", String.valueOf(id)); - valueMap.put("assertionConsumerServiceURL", String.valueOf(settings.getSpAssertionConsumerServiceUrl())); - valueMap.put("protocolBinding", settings.getSpAssertionConsumerServiceBinding()); - valueMap.put("spEntityid", settings.getSpEntityId()); + valueMap.put("id", Util.toXml(String.valueOf(id))); + valueMap.put("assertionConsumerServiceURL", Util.toXml(String.valueOf(settings.getSpAssertionConsumerServiceUrl()))); + valueMap.put("protocolBinding", Util.toXml(settings.getSpAssertionConsumerServiceBinding())); + valueMap.put("spEntityid", Util.toXml(settings.getSpEntityId())); String requestedAuthnContextStr = ""; List requestedAuthnContexts = settings.getRequestedAuthnContext(); if (requestedAuthnContexts != null && !requestedAuthnContexts.isEmpty()) { String requestedAuthnContextCmp = settings.getRequestedAuthnContextComparison(); - requestedAuthnContextStr = ""; + requestedAuthnContextStr = ""; for (String requestedAuthnContext : requestedAuthnContexts) { - requestedAuthnContextStr += "" + requestedAuthnContext + ""; + requestedAuthnContextStr += "" + Util.toXml(requestedAuthnContext) + ""; } requestedAuthnContextStr += ""; } 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 9caa9b39..721cc293 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -282,7 +282,7 @@ public String getLogoutRequestXml() { private StrSubstitutor generateSubstitutor(Saml2Settings settings) { Map valueMap = new HashMap(); - valueMap.put("id", id); + valueMap.put("id", Util.toXml(id)); String issueInstantString = Util.formatDateTime(issueInstant.getTimeInMillis()); valueMap.put("issueInstant", issueInstantString); @@ -290,11 +290,11 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { String destinationStr = ""; URL slo = settings.getIdpSingleLogoutServiceUrl(); if (slo != null) { - destinationStr = " Destination=\"" + slo.toString() + "\""; + destinationStr = " Destination=\"" + Util.toXml(slo.toString()) + "\""; } valueMap.put("destinationStr", destinationStr); - valueMap.put("issuer", settings.getSpEntityId()); + valueMap.put("issuer", Util.toXml(settings.getSpEntityId())); String nameIdFormat = null; String spNameQualifier = this.nameIdSPNameQualifier; @@ -337,7 +337,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { String sessionIndexStr = ""; if (sessionIndex != null) { - sessionIndexStr = " " + sessionIndex + ""; + sessionIndexStr = " " + Util.toXml(sessionIndex) + ""; } valueMap.put("sessionIndexStr", sessionIndexStr); 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 e943d826..85c9264f 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -397,7 +397,7 @@ public void build() { private StrSubstitutor generateSubstitutor(Saml2Settings settings, String statusCode) { Map valueMap = new HashMap(); - valueMap.put("id", id); + valueMap.put("id", Util.toXml(id)); String issueInstantString = Util.formatDateTime(issueInstant.getTimeInMillis()); valueMap.put("issueInstant", issueInstantString); @@ -405,23 +405,23 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings, String status String destinationStr = ""; URL slo = settings.getIdpSingleLogoutServiceResponseUrl(); if (slo != null) { - destinationStr = " Destination=\"" + slo.toString() + "\""; + destinationStr = " Destination=\"" + Util.toXml(slo.toString()) + "\""; } valueMap.put("destinationStr", destinationStr); String inResponseStr = ""; if (inResponseTo != null) { - inResponseStr = " InResponseTo=\"" + inResponseTo + "\""; + inResponseStr = " InResponseTo=\"" + Util.toXml(inResponseTo) + "\""; } valueMap.put("inResponseStr", inResponseStr); String statusStr = ""; if (statusCode != null) { - statusStr = "Value=\"" + statusCode + "\""; + statusStr = "Value=\"" + Util.toXml(statusCode) + "\""; } valueMap.put("statusStr", statusStr); - valueMap.put("issuer", settings.getSpEntityId()); + valueMap.put("issuer", Util.toXml(settings.getSpEntityId())); return new StrSubstitutor(valueMap); } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 7d6e8d91..e5ee5c17 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -1,5 +1,7 @@ package com.onelogin.saml2.settings; +import static com.onelogin.saml2.util.Util.toXml; + import java.net.URL; import java.util.Arrays; import java.util.Calendar; @@ -126,7 +128,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif Map valueMap = new HashMap(); Boolean wantsEncrypted = settings.getWantAssertionsEncrypted() || settings.getWantNameIdEncrypted(); - valueMap.put("id", Util.generateUniqueID(settings.getUniqueIDPrefix())); + valueMap.put("id", Util.toXml(Util.generateUniqueID(settings.getUniqueIDPrefix()))); String validUntilTimeStr = ""; if (validUntilTime != null) { String validUntilTimeValue = Util.formatDateTime(validUntilTime.getTimeInMillis()); @@ -141,12 +143,12 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif } valueMap.put("cacheDurationStr", cacheDurationStr); - valueMap.put("spEntityId", settings.getSpEntityId()); + valueMap.put("spEntityId", Util.toXml(settings.getSpEntityId())); valueMap.put("strAuthnsign", String.valueOf(settings.getAuthnRequestsSigned())); valueMap.put("strWsign", String.valueOf(settings.getWantAssertionsSigned())); - valueMap.put("spNameIDFormat", settings.getSpNameIDFormat()); - valueMap.put("spAssertionConsumerServiceBinding", settings.getSpAssertionConsumerServiceBinding()); - valueMap.put("spAssertionConsumerServiceUrl", settings.getSpAssertionConsumerServiceUrl().toString()); + valueMap.put("spNameIDFormat", Util.toXml(settings.getSpNameIDFormat())); + valueMap.put("spAssertionConsumerServiceBinding", Util.toXml(settings.getSpAssertionConsumerServiceBinding())); + valueMap.put("spAssertionConsumerServiceUrl", Util.toXml(settings.getSpAssertionConsumerServiceUrl().toString())); valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); valueMap.put("strAttributeConsumingService", getAttributeConsumingServiceXml()); @@ -198,10 +200,10 @@ private String getAttributeConsumingServiceXml() { attributeConsumingServiceXML.append(""); if (serviceName != null && !serviceName.isEmpty()) { - attributeConsumingServiceXML.append("" + serviceName + ""); + attributeConsumingServiceXML.append("" + Util.toXml(serviceName) + ""); } if (serviceDescription != null && !serviceDescription.isEmpty()) { - attributeConsumingServiceXML.append("" + serviceDescription + ""); + attributeConsumingServiceXML.append("" + Util.toXml(serviceDescription) + ""); } if (requestedAttributes != null && !requestedAttributes.isEmpty()) { for (RequestedAttribute requestedAttribute : requestedAttributes) { @@ -214,15 +216,15 @@ private String getAttributeConsumingServiceXml() { String contentStr = "" + attrValue + ""; + contentStr += "" + Util.toXml(attrValue) + ""; } attributeConsumingServiceXML.append(contentStr + ""); } else { @@ -256,9 +258,9 @@ private String toContactsXml(List contacts) { StringBuilder contactsXml = new StringBuilder(); for (Contact contact : contacts) { - contactsXml.append(""); - contactsXml.append("" + contact.getGivenName() + ""); - contactsXml.append("" + contact.getEmailAddress() + ""); + contactsXml.append(""); + contactsXml.append("" + Util.toXml(contact.getGivenName()) + ""); + contactsXml.append("" + Util.toXml(contact.getEmailAddress()) + ""); contactsXml.append(""); } @@ -276,10 +278,10 @@ private String toOrganizationXml(Organization organization) { if (organization != null) { String lang = organization.getOrgLangAttribute(); - orgXml = "" + organization.getOrgName() - + "" - + organization.getOrgDisplayName() + "" + organization.getOrgUrl() + ""; + orgXml = "" + Util.toXml(organization.getOrgName()) + + "" + + Util.toXml(organization.getOrgDisplayName()) + "" + Util.toXml(organization.getOrgUrl()) + ""; } return orgXml; } @@ -316,7 +318,7 @@ private String toX509KeyDescriptorsXML(X509Certificate certCurrent, X509Certific keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+certString+""); + keyDescriptorXml.append(""+Util.toXml(certString)+""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); @@ -325,7 +327,7 @@ private String toX509KeyDescriptorsXML(X509Certificate certCurrent, X509Certific keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+certString+""); + keyDescriptorXml.append(""+Util.toXml(certString)+""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); @@ -343,8 +345,8 @@ private String toSLSXml(URL spSingleLogoutServiceUrl, String spSingleLogoutServi StringBuilder slsXml = new StringBuilder(); if (spSingleLogoutServiceUrl != null) { - slsXml.append(""); + slsXml.append(""); } return slsXml.toString(); } 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 1c32e5e4..d391afa1 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -63,9 +63,9 @@ import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactoryConfigurationException; -import com.onelogin.saml2.model.hsm.HSM; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.xml.security.encryption.EncryptedData; import org.apache.xml.security.encryption.EncryptedKey; @@ -95,6 +95,7 @@ import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.model.SamlResponseStatus; +import com.onelogin.saml2.model.hsm.HSM; /** @@ -445,7 +446,7 @@ public static String convertDocumentToString(Document doc, Boolean c14n) { * @return the Document object */ public static String convertDocumentToString(Document doc) { - return convertDocumentToString(doc, false); + return convertDocumentToString(doc, false); } /** @@ -1984,6 +1985,17 @@ public static DateTime parseDateTime(String dateTime) { } return parsedData; } + + /** + * Escape a text so that it can be safely used within an XML element contents or attribute value. + * + * @param text + * the text to escape + * @return the escaped text (null if the input is null) + */ + public static String toXml(String text) { + return StringEscapeUtils.escapeXml10(text); + } private static String toStringUtf8(byte[] bytes) { try { diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index d08b5768..e776044c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -100,6 +100,32 @@ public void testGetEncodedAuthnRequestOnlySettings() throws Exception { assertThat(authnRequestStr, containsString("ProviderName=\"SP Java Example\"")); } + /** + * Tests the getEncodedAuthnRequest method of AuthnRequest + *

+ * Case: Only settings provided and containing special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest#getEncodedAuthnRequest + */ + @Test + public void testGetEncodedAuthnRequestOnlySettingsSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString("urn:oasis:names:tc:SAML:2.0:ac:classes:X509")); } + /** + * Tests the AuthnRequest Constructor + * The creation of a deflated SAML Request with and without AuthNContext + *

+ * Case: AuthnContextClassRef contains custom URN with special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testAuthNContextSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + + List requestedAuthnContext = new ArrayList(); + settings.setRequestedAuthnContext(requestedAuthnContext); + + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString("")); + assertThat(authnRequestStr, containsString("urn:custom:a&b")); + + requestedAuthnContext.add("urn:oasis:names:tc:SAML:2.0:ac:classes:X509"); + settings.setRequestedAuthnContext(requestedAuthnContext); + settings.setRequestedAuthnContext(requestedAuthnContext); + authnRequest = new AuthnRequest(settings); + authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString("")); + assertThat(authnRequestStr, containsString("urn:custom:a&b")); + assertThat(authnRequestStr, containsString("urn:oasis:names:tc:SAML:2.0:ac:classes:X509")); + } + /** * Tests the AuthnRequest Constructor * The creation of a deflated SAML Request with and without Subject @@ -308,6 +378,35 @@ public void testSubject() throws Exception { assertThat(authnRequestStr, containsString("")); } + /** + * Tests the AuthnRequest Constructor + * The creation of a deflated SAML Request with and without Subject + *

+ * Case: subject contains special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testSubjectSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString("t&stuser@example.com")); + assertThat(authnRequestStr, containsString("")); + } + /** * Tests the getId method of AuthnRequest * @@ -352,4 +451,32 @@ public void testAuthNDestination() throws Exception { assertThat(authnRequestStr, containsString(" + * Case: destinations contain special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testAuthNDestinationSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString(" + * Case: session index. * * @throws Exception * @@ -166,6 +167,33 @@ public void testConstructorWithSessionIndex() throws Exception { assertThat(logoutRequestStr, not(containsString(""))); } + /** + * Tests the constructor and the getEncodedAuthnRequest method of LogoutRequest + *

+ * Case: session index containing special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutRequest + */ + @Test + public void testConstructorWithSessionIndexSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String sessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf&"; + LogoutRequest logoutRequest = new LogoutRequest(settings, null, null, sessionIndex); + String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); + String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); + String expectedSessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf&"; + assertThat(logoutRequestStr, containsString(""))); + } + /** * Tests the constructor and the getEncodedAuthnRequest method of LogoutRequest * Case: encrypted NameId @@ -202,6 +230,7 @@ public void testGetLogoutRequestXml() throws Exception { LogoutRequest logoutRequest = new LogoutRequest(settings); String logoutRequestXML = logoutRequest.getLogoutRequestXml(); assertThat(logoutRequestXML, containsString(" + * Case: logout destination contains special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutRequest#getLogoutRequestXml + */ + @Test + public void testGetLogoutRequestXmlSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + LogoutRequest logoutRequest = new LogoutRequest(settings); + String logoutRequestXML = logoutRequest.getLogoutRequestXml(); + assertThat(logoutRequestXML, containsString(" + * Case: Able to get the NameIdData. * * @throws Exception * @@ -288,6 +335,34 @@ public void testGetNameIdData() throws Exception { assertThat(nameIdDataStr, containsString("SPNameQualifier=" + settings.getSpEntityId())); } + /** + * Tests the getNameIdData method of LogoutRequest + *

+ * Case: Able to get the NameIdData, containing special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutRequest#getNameIdData + */ + @Test + public void testGetNameIdDataSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + LogoutRequest logoutRequest = new LogoutRequest(settings, null, "", null); + String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); + String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); + assertThat(logoutRequestStr, containsString("<ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c>")); + assertThat(logoutRequestStr, not(containsString("SPNameQualifier"))); + + logoutRequest = new LogoutRequest(settings, null, null, null); + logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); + logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); + assertThat(logoutRequestStr, containsString("http://idp.example.com/?a=1&b=2")); + assertThat(logoutRequestStr, not(containsString("SPNameQualifier"))); + } + /** * Tests the getId method of LogoutRequest * diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index fe04cb7c..b95f60f2 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -175,12 +175,39 @@ public void testBuild() throws IOException, XMLEntityException, URISyntaxExcepti * @see com.onelogin.saml2.logout.LogoutResponse#getLogoutResponseXml */ @Test - public void testGetLogoutRequestXml() throws Exception { + public void testGetLogoutResponseXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); LogoutResponse logoutResponse = new LogoutResponse(settings, null); logoutResponse.build(); String logoutResponseXML = logoutResponse.getLogoutResponseXml(); assertThat(logoutResponseXML, containsString(" + * Case: logout destination contains special chars. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutResponse#getLogoutResponseXml + */ + @Test + public void testGetLogoutResponseXmlSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + LogoutResponse logoutResponse = new LogoutResponse(settings, null); + logoutResponse.build(); + String logoutResponseXML = logoutResponse.getLogoutResponseXml(); + assertThat(logoutResponseXML, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } + /** + * Tests the constructor method of Metadata + *

+ * Case: main SP text containing special chars. + * + * @throws Exception + * @see com.onelogin.saml2.settings.Metadata + */ + @Test + public void testMetadataSpecialChars() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + Document metadataDoc = Util.loadXML(metadataStr); + + assertTrue(metadataDoc instanceof Document); + + assertEquals("md:EntityDescriptor", metadataDoc.getDocumentElement().getNodeName()); + assertEquals("md:SPSSODescriptor", metadataDoc.getDocumentElement().getFirstChild().getNodeName()); + + assertTrue(Util.validateXML(metadataDoc, SchemaFactory.SAML_SCHEMA_METADATA_2_0)); + + assertThat(metadataStr, containsString(""))); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); + } + /** * Tests the constructor method of Metadata (Expiration) * @@ -124,6 +157,26 @@ public void testToContactsXml() throws IOException, CertificateEncodingException assertThat(metadataStr2, not(containsString(contactStr))); } + /** + * Tests the toContactsXml method of Metadata + *

+ * Case: contacts text containing special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toContactsXml + */ + @Test + public void testToContactsXmlSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllSpecialCharsProperties(); + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + + String contactStr = "T&chnical Guyt&chnical@example.com"Support Guy"supp&rt@example.com"; + assertThat(metadataStr, containsString(contactStr)); + } + /** * Tests the toOrganizationXml method of Metadata (Expiration) * @@ -134,7 +187,7 @@ public void testToContactsXml() throws IOException, CertificateEncodingException */ @Test public void testToOrganizationXml() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); + Saml2Settings settings = getSettingFromAllProperties(); Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); @@ -197,6 +250,26 @@ public void testToLocalizedOrganizationXml() throws IOException, CertificateEnco assertThat(metadataStr2, not(containsString(orgStr))); } + /** + * Tests the toOrganizationXml method of Metadata (Expiration) + *

+ * Case: organization text containing special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toOrganizationXml + */ + @Test + public void testToOrganizationXmlSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllSpecialCharsProperties(); + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + + String orgStr = "S&P JavaS&P Java "Example"http://sp.example.com?a=1&b=2"; + assertThat(metadataStr, containsString(orgStr)); + } + /** * Tests the toSLSXml method of Metadata * @@ -222,6 +295,27 @@ public void testToSLSXml() throws IOException, CertificateEncodingException, Err assertThat(metadataStr2, not(containsString(slsStr))); } + /** + * Tests the toSLSXml method of Metadata + *

+ * Case: SLS definition containing special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toSLSXml + */ + @Test + public void testToSLSXmlSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllSpecialCharsProperties(); + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + + String slsStr = ""; + + assertThat(metadataStr, containsString(slsStr)); + } + /** * Tests the toX509KeyDescriptorsXML method of Metadata * @@ -338,7 +432,47 @@ public void testGetAttributeConsumingServiceXml() throws IOException, Certificat /** * Tests the getAttributeConsumingServiceXml method of Metadata - * Case: AttributeConsumingService Multiple AttributeValue + *

+ * Case: Attribute Consuming Service definition contains special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml + */ + @Test + public void testGetAttributeConsumingServiceXmlSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllProperties(); + + AttributeConsumingService attributeConsumingService = new AttributeConsumingService("T&st Service", "T&st Service Desc"); + RequestedAttribute requestedAttribute = new RequestedAttribute("Email", "Email \"address\"", true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); + RequestedAttribute requestedAttribute2 = new RequestedAttribute("FirstName&LastName", null, true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); + + attributeConsumingService.addRequestedAttribute(requestedAttribute); + attributeConsumingService.addRequestedAttribute(requestedAttribute2); + + Metadata metadataObj = new Metadata(settings, null, null, attributeConsumingService); + String metadataStr = metadataObj.getMetadataString(); + + String headerStr = ""; + String sNameStr = "T&st Service"; + String sDescStr = "T&st Service Desc"; + String reqAttr1Str = ""; + String reqAttr2Str = ""; + String footerStr = ""; + + assertThat(metadataStr, containsString(headerStr)); + assertThat(metadataStr, containsString(sNameStr)); + assertThat(metadataStr, containsString(sDescStr)); + assertThat(metadataStr, containsString(reqAttr1Str)); + assertThat(metadataStr, containsString(reqAttr2Str)); + assertThat(metadataStr, containsString(footerStr)); + } + + /** + * Tests the getAttributeConsumingServiceXml method of Metadata + *

+ * Case: AttributeConsumingService Multiple AttributeValue. * * @throws IOException * @throws CertificateEncodingException @@ -381,6 +515,52 @@ public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() thro assertThat(metadataStr, containsString(footerStr)); } + /** + * Tests the getAttributeConsumingServiceXml method of Metadata + *

+ * Case: AttributeConsumingService Multiple AttributeValue with special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml + */ + @Test + public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValueSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllProperties(); + + AttributeConsumingService attributeConsumingService = new AttributeConsumingService("T&st Service", "T&st Service Desc"); + List attrValues = new ArrayList(); + attrValues.add("us&rType"); + attrValues.add("admin"); + RequestedAttribute requestedAttribute = new RequestedAttribute("us&rType", null, false, "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", attrValues); + RequestedAttribute requestedAttribute2 = new RequestedAttribute("urn:oid:0.9.2342.19200300.100.1.1", "uid", true, "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", null); + + attributeConsumingService.addRequestedAttribute(requestedAttribute); + attributeConsumingService.addRequestedAttribute(requestedAttribute2); + + Metadata metadataObj = new Metadata(settings, null, null, attributeConsumingService); + String metadataStr = metadataObj.getMetadataString(); + + String headerStr = ""; + String sNameStr = "T&st Service"; + String sDescStr = "T&st Service Desc"; + String reqAttr1Str = ""; + String reqAttr1Atr1Str = "us&rType"; + String reqAttr1Attr2Str = "admin"; + String reqAttr2Str = ""; + String footerStr = ""; + + assertThat(metadataStr, containsString(headerStr)); + assertThat(metadataStr, containsString(sNameStr)); + assertThat(metadataStr, containsString(sDescStr)); + assertThat(metadataStr, containsString(reqAttr1Str)); + assertThat(metadataStr, containsString(reqAttr1Atr1Str)); + assertThat(metadataStr, containsString(reqAttr1Attr2Str)); + assertThat(metadataStr, containsString(reqAttr2Str)); + assertThat(metadataStr, containsString(footerStr)); + } + /** * Tests the signMetadata method of Metadata * Case imported metadata @@ -462,6 +642,10 @@ private Saml2Settings getSettingFromAllProperties() throws Error, IOException { return new SettingsBuilder().fromFile("config/config.all.properties").build(); } + private Saml2Settings getSettingFromAllSpecialCharsProperties() throws Error, IOException { + return new SettingsBuilder().fromFile("config/config.all_specialchars.properties").build(); + } + @Test public void shouldIncludeValidUntilAndDuration() throws CertificateEncodingException, Error, IOException { //given diff --git a/core/src/test/resources/config/config.all_specialchars.properties b/core/src/test/resources/config/config.all_specialchars.properties new file mode 100644 index 00000000..a4492871 --- /dev/null +++ b/core/src/test/resources/config/config.all_specialchars.properties @@ -0,0 +1,155 @@ +# If 'strict' is True, then the Java Toolkit will reject unsigned +# or unencrypted messages if it expects them signed or encrypted +# Also will reject the messages if not strictly follow the SAML +onelogin.saml2.strict = true + +# Enable debug mode (to print errors) +onelogin.saml2.debug = true + +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp?a=1&b=2 +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp?a=1&b=2 +# SAML protocol binding to be used when returning the or sending the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-POST binding only +onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp?a=1&b=2 + +# SAML protocol binding for the Single Logout Service of the SP. +# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Specifies constraints on the name identifier to be used to +# represent the requested subject. +# Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported +onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + +# Usually x509cert and privateKey of the SP are provided by files placed at +# the certs folder. But we can also provide them with the following parameters +onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- + +# To be used during SP Key roll over +onelogin.saml2.sp.x509certNew = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- + +# Requires Format PKCS#8 BEGIN PRIVATE KEY +# If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem +onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- + +# Identity Provider Data that we want connect with our SP +# Identifier of the IdP entity (must be a URI) +onelogin.saml2.idp.entityid = http://idp.example.com/ + +# SSO endpoint info of the IdP. (Authentication Request protocol) +# URL Target of the IdP where the SP will send the Authentication Request Message +onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php + +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# SLO endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Request +onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php + +# Optional SLO Response endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Response. If left blank, same URL as onelogin.saml2.idp.single_logout_service.url will be used. +# Some IdPs use a separate URL for sending a logout request and response, use this property to set the separate response url +onelogin.saml2.idp.single_logout_service.response.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutServiceResponse.php + +# SAML protocol binding to be used when returning the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Public x509 certificate of the IdP +onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==\n-----END CERTIFICATE----- +onelogin.saml2.idp.certfingerprint = 4b6f70bb2cab82c86a8270f71a880b62e25bc2b3 +onelogin.saml2.idp.certfingerprint_algorithm = sha1 + +# Security settings +# + +# Indicates that the nameID of the sent by this SP +# will be encrypted. +onelogin.saml2.security.nameid_encrypted = true + +# Indicates whether the messages sent by this SP +# will be signed. [The Metadata of the SP will offer this info] +onelogin.saml2.security.authnrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutresponse_signed = true + +# Indicates a requirement for the , and +# elements received by this SP to be signed. +onelogin.saml2.security.want_messages_signed = true + +# Indicates a requirement for the of the to be signed +onelogin.saml2.security.want_assertions_signed = true + +# Indicates a requirement for the Metadata of this SP to be signed. +# Right now supported null/false (in order to not sign) or true (sign using SP private key) +onelogin.saml2.security.sign_metadata = true + +# Indicates a requirement for the Assertions received by this SP to be encrypted +onelogin.saml2.security.want_assertions_encrypted = true + +# Indicates a requirement for the NameID received by this SP to be encrypted +onelogin.saml2.security.want_nameid_encrypted = true + +# Authentication context. +# Set Empty and no AuthContext will be sent in the AuthNRequest, +# Set comma separated values urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password +onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password + +# Allows the authn comparison parameter to be set, defaults to 'exact' +onelogin.saml2.security.requested_authncontextcomparison = exact + + +# Indicates if the SP will validate all received xmls. +# (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). +onelogin.saml2.security.want_xml_validation = true + +# Algorithm that the toolkit will use on signing process. Options: +# 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' +onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 + +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + +# Organization +onelogin.saml2.organization.name = S&P Java +onelogin.saml2.organization.displayname = S&P Java "Example" +onelogin.saml2.organization.url = http://sp.example.com?a=1&b=2 +onelogin.saml2.organization.lang = en + +# Contacts +onelogin.saml2.contacts.technical.given_name = T&chnical Guy +onelogin.saml2.contacts.technical.email_address = t&chnical@example.com +onelogin.saml2.contacts.support.given_name = "Support Guy" +onelogin.saml2.contacts.support.email_address = supp&rt@example.com + +# Prefix used in generated Unique IDs. +# Optional, defaults to ONELOGIN_ or full ID is like ONELOGIN_ebb0badd-4f60-4b38-b20a-a8e01f0592b1. +# At minimun, the prefix can be non-numeric character such as "_". +onelogin.saml2.unique_id_prefix = EXAMPLE diff --git a/core/src/test/resources/config/config.min_specialchars.properties b/core/src/test/resources/config/config.min_specialchars.properties new file mode 100644 index 00000000..c683c78c --- /dev/null +++ b/core/src/test/resources/config/config.min_specialchars.properties @@ -0,0 +1,26 @@ +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp?a=1&b=2 +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp?a=1&b=2 + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp?a=1&b=2 + +# Identity Provider Data that we want connect with our SP +# Identifier of the IdP entity (must be a URI) +onelogin.saml2.idp.entityid = http://idp.example.com/?a=1&b=2 + +# SSO endpoint info of the IdP. (Authentication Request protocol) +# URL Target of the IdP where the SP will send the Authentication Request Message +onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php?a=1&b=2 + +# SLO endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Request +onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php?a=1&b=2 + +# Public x509 certificate of the IdP +onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==\n-----END CERTIFICATE----- \ No newline at end of file From 88ffba625a5b4aaa998571f3472adafb19b13bb8 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 31 Mar 2021 19:23:15 +0200 Subject: [PATCH 051/133] Clarify the real nature of the RelayState parameter for SSO and SLO In the most simple case, the RelayState may be used as a "returnUrl", but I think it's important to underline that the RelayState does not necessarily need to be a return URL. Indeed, the SAML 2.0 specification clarifies that a limit of max 80 characters exists for it (at least in the case of the HTTP-Redirect binding) and that a protection method against tampering is suggested. Therefore, a return URL in general would probably be a non-ideal use of the RelayState parameter, so let's give the latter the relevance it deserves. --- README.md | 12 +- .../main/java/com/onelogin/saml2/Auth.java | 321 ++++++++++++------ 2 files changed, 232 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 14d82177..bb708568 100644 --- a/README.md +++ b/README.md @@ -433,10 +433,10 @@ The AuthNRequest will be sent signed or unsigned based on the security settings The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. -We can set a 'returnTo' url parameter to the login function and that will be converted as a 'RelayState' parameter: +We can set a 'RelayState' parameter containing a return url to the login function: ``` -String targetUrl = 'https://example.com'; -auth.login(returnTo=targetUrl) +String returnUrl = 'https://example.com'; +auth.login(relayState=returnUrl) ``` The login method can receive 6 more optional parameters: - *forceAuthn* When true the AuthNRequest will have the 'ForceAuthn' attribute set to 'true' @@ -605,10 +605,10 @@ The Logout Request will be sent signed or unsigned based on the security setting The IdP will return the Logout Response through the user's client to the Single Logout Service of the SP. -We can set a 'returnTo' url parameter to the logout function and that will be converted as a 'RelayState' parameter: +We can set a 'RelayState' parameter containing a return url to the login function: ``` -String targetUrl = 'https://example.com'; -auth.logout(returnTo=targetUrl) +String returnUrl = 'https://example.com'; +auth.logout(relayState=returnUrl) ``` Also there are 7 optional parameters that can be set: diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 092f9324..5711ef70 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -310,10 +310,19 @@ public void setStrict(Boolean value) { /** * Initiates the SSO process. * - * @param returnTo The target URL the user should be returned to after - * login (relayState). Will be a self-routed URL when - * null, or not be appended at all when an empty string - * is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the authenticated user should be redirected after the + * authentication response has been received back from the + * Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param forceAuthn When true the AuthNRequest will set the * ForceAuthn='true' * @param isPassive When true the AuthNRequest will set the @@ -329,20 +338,29 @@ public void setStrict(Boolean value) { * @throws IOException * @throws SettingsException */ - public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, + public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, String nameIdValueReq) throws IOException, SettingsException { Map parameters = new HashMap(); - return login(returnTo, forceAuthn, isPassive, setNameIdPolicy, stay, + return login(relayState, forceAuthn, isPassive, setNameIdPolicy, stay, nameIdValueReq, parameters); } /** * Initiates the SSO process. * - * @param returnTo The target URL the user should be returned to after - * login (relayState). Will be a self-routed URL when - * null, or not be appended at all when an empty string - * is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the authenticated user should be redirected after the + * authentication response has been received back from the + * Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param forceAuthn When true the AuthNRequest will set the * ForceAuthn='true' * @param isPassive When true the AuthNRequest will set the @@ -359,7 +377,7 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool * @throws IOException * @throws SettingsException */ - public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, + public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, String nameIdValueReq, Map parameters) throws IOException, SettingsException { AuthnRequest authnRequest = new AuthnRequest(settings, forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq); @@ -371,11 +389,8 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool parameters.put("SAMLRequest", samlRequest); - String relayState; - if (returnTo == null) { + if (relayState == null) { relayState = ServletUtils.getSelfRoutedURLNoQuery(request); - } else { - relayState = returnTo; } if (!relayState.isEmpty()) { @@ -403,10 +418,19 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool /** * Initiates the SSO process. * - * @param returnTo The target URL the user should be returned to after - * login (relayState). Will be a self-routed URL when - * null, or not be appended at all when an empty string - * is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the authenticated user should be redirected after the + * authentication response has been received back from the + * Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param forceAuthn When true the AuthNRequest will set the * ForceAuthn='true' * @param isPassive When true the AuthNRequest will set the @@ -420,18 +444,27 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool * @throws IOException * @throws SettingsException */ - public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay) + public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay) throws IOException, SettingsException { - return login(returnTo, forceAuthn, isPassive, setNameIdPolicy, stay, null); + return login(relayState, forceAuthn, isPassive, setNameIdPolicy, stay, null); } /** * Initiates the SSO process. * - * @param returnTo The target URL the user should be returned to after - * login (relayState). Will be a self-routed URL when - * null, or not be appended at all when an empty string - * is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the authenticated user should be redirected after the + * authentication response has been received back from the + * Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param forceAuthn When true the AuthNRequest will set the * ForceAuthn='true' * @param isPassive When true the AuthNRequest will set the @@ -441,9 +474,9 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool * @throws IOException * @throws SettingsException */ - public void login(String returnTo, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy) + public void login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy) throws IOException, SettingsException { - login(returnTo, forceAuthn, isPassive, setNameIdPolicy, false); + login(relayState, forceAuthn, isPassive, setNameIdPolicy, false); } /** @@ -459,24 +492,43 @@ public void login() throws IOException, SettingsException { /** * Initiates the SSO process. * - * @param returnTo The target URL the user should be returned to after login - * (relayState). Will be a self-routed URL when null, or not be - * appended at all when an empty string is provided. + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the authenticated user should be redirected after the + * authentication response has been received back from the + * Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * * @throws IOException * @throws SettingsException */ - public void login(String returnTo) throws IOException, SettingsException { - login(returnTo, false, false, true); + public void login(String relayState) throws IOException, SettingsException { + login(relayState, false, false, true); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to - * after logout (relayState). Will be a self-routed - * URL when null, or not be appended at all when an - * empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the * LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response @@ -496,21 +548,30 @@ public void login(String returnTo) throws IOException, SettingsException { * @throws XMLEntityException * @throws SettingsException */ - public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) throws IOException, XMLEntityException, SettingsException { Map parameters = new HashMap(); - return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, + return logout(relayState, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier, parameters); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to - * after logout (relayState). Will be a self-routed - * URL when null, or not be appended at all when an - * empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the * LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response @@ -523,7 +584,7 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * LogoutRequest. * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in * the LogoutRequest. - * @param parameters Use it to send extra parameters in addition to the LogoutRequest + * @param parameters Use it to send extra parameters in addition to the LogoutRequest * * @return the SLO URL with the LogoutRequest if stay = True * @@ -531,7 +592,7 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @throws XMLEntityException * @throws SettingsException */ - public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier, Map parameters) throws IOException, XMLEntityException, SettingsException { @@ -544,11 +605,8 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea String samlLogoutRequest = logoutRequest.getEncodedLogoutRequest(); parameters.put("SAMLRequest", samlLogoutRequest); - String relayState; - if (returnTo == null) { + if (relayState == null) { relayState = ServletUtils.getSelfRoutedURLNoQuery(request); - } else { - relayState = returnTo; } if (!relayState.isEmpty()) { @@ -576,10 +634,19 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to - * after logout (relayState). Will be a self-routed - * URL when null, or not be appended at all when an - * empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in * the SSO process). @@ -596,18 +663,27 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @throws XMLEntityException * @throws SettingsException */ - public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier) throws IOException, XMLEntityException, SettingsException { - return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, null); + return logout(relayState, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, null); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to after - * logout (relayState). Will be a self-routed URL when null, - * or not be appended at all when an empty string is - * provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO * process). @@ -621,18 +697,27 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @throws XMLEntityException * @throws SettingsException */ - public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat) + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat) throws IOException, XMLEntityException, SettingsException { - return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, null); + return logout(relayState, nameId, sessionIndex, stay, nameidFormat, null); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to after - * logout (relayState). Will be a self-routed URL when null, - * or not be appended at all when an empty string is - * provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO * process). @@ -645,18 +730,27 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @throws XMLEntityException * @throws SettingsException */ - public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay) + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay) throws IOException, XMLEntityException, SettingsException { - return logout(returnTo, nameId, sessionIndex, stay, null); + return logout(relayState, nameId, sessionIndex, stay, null); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to - * after logout (relayState). Will be a self-routed - * URL when null, or not be appended at all when an - * empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the * LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response @@ -671,19 +765,28 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat, + public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) throws IOException, XMLEntityException, SettingsException { - logout(returnTo, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier); + logout(relayState, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to - * after logout (relayState). Will be a self-routed - * URL when null, or not be appended at all when an - * empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in * the SSO process). @@ -696,18 +799,27 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat, + public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat, String nameIdNameQualifier) throws IOException, XMLEntityException, SettingsException { - logout(returnTo, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier); + logout(relayState, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to after - * logout (relayState). Will be a self-routed URL when null, - * or not be appended at all when an empty string is - * provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO * process). @@ -716,18 +828,27 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat) + public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat) throws IOException, XMLEntityException, SettingsException { - logout(returnTo, nameId, sessionIndex, false, nameidFormat); + logout(relayState, nameId, sessionIndex, false, nameidFormat); } /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to after - * logout (relayState). Will be a self-routed URL when null, - * or not be appended at all when an empty string is - * provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * @param nameId The NameID that will be set in the LogoutRequest. * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO * process). @@ -736,9 +857,9 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo, String nameId, String sessionIndex) + public void logout(String relayState, String nameId, String sessionIndex) throws IOException, XMLEntityException, SettingsException { - logout(returnTo, nameId, sessionIndex, false, null); + logout(relayState, nameId, sessionIndex, false, null); } /** @@ -755,16 +876,26 @@ public void logout() throws IOException, XMLEntityException, SettingsException { /** * Initiates the SLO process. * - * @param returnTo The target URL the user should be returned to after logout - * (relayState). Will be a self-routed URL when null, or not be - * appended at all when an empty string is provided + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided * * @throws IOException * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo) throws IOException, XMLEntityException, SettingsException { - logout(returnTo, null, null); + public void logout(String relayState) throws IOException, XMLEntityException, SettingsException { + logout(relayState, null, null); } /** From 8ba6ba094b2e9a4bdc3f6470368249ffef4b3133 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 1 Apr 2021 10:41:15 +0200 Subject: [PATCH 052/133] Add tests specific to the new Util.toXml method --- .../onelogin/saml2/test/util/UtilsTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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 b8eb5b97..2b047bc1 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 @@ -2146,4 +2146,27 @@ public void testQuery() throws XPathExpressionException, URISyntaxException, IOE assertEquals("saml2:Assertion", assertion_2.getNodeName()); } + /** + * Tests the toXml method + * + * @see com.onelogin.saml2.util.Util#toXml(String) + */ + @Test + public void testToXml() { + assertEquals("", Util.toXml("")); + assertEquals("No escape", Util.toXml("No escape")); + assertEquals("&"<>'", Util.toXml("&\"<>'")); + } + + /** + * Tests the toXml method + *

+ * Case: the input is null. + * + * @see com.onelogin.saml2.util.Util#toXml(String) + */ + @Test + public void testToXmlNull() { + assertNull(Util.toXml(null)); + } } From 6589f28b99076fca96d085784c2a76a79a5d3990 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 1 Apr 2021 11:37:06 +0200 Subject: [PATCH 053/133] Make SamlResponse more extensible The query and queryAssertion methods are really useful when creating extensions of SamlResponse that need to expose other information that the plain java-saml library does not (like the response issue instant, or any custom contents of the AuthnContext in the assertion auth statement). Having these two methods private forces the extender to reimplement them from scratch, probably by copy-and-paste, which is clearly sub-optimal. --- .../java/com/onelogin/saml2/authn/SamlResponse.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) 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 7f38b81c..d3790f5a 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -1033,7 +1033,7 @@ public Exception getValidationException() { * @throws XPathExpressionException * */ - private NodeList queryAssertion(String assertionXpath) throws XPathExpressionException { + protected NodeList queryAssertion(String assertionXpath) throws XPathExpressionException { final String assertionExpr = "/saml:Assertion"; final String signatureExpr = "ds:Signature/ds:SignedInfo/ds:Reference"; @@ -1084,16 +1084,9 @@ private NodeList queryAssertion(String assertionXpath) throws XPathExpressionExc * * @return DOMNodeList The queried nodes */ - private NodeList query(String nameQuery, Node context) throws XPathExpressionException { - Document doc; - if (encrypted) { - doc = decryptedDocument; - } else { - doc = samlResponseDocument; - } - + protected NodeList query(String nameQuery, Node context) throws XPathExpressionException { // LOGGER.debug("Executing query " + nameQuery); - return Util.query(doc, nameQuery, context); + return Util.query(getSAMLResponseDocument(), nameQuery, context); } /** From c98ad807f50c0af16f789b91fcae499c0a5a0927 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 1 Apr 2021 12:33:51 +0200 Subject: [PATCH 054/133] Fix Util.getSignatureData javadoc --- .../java/com/onelogin/saml2/util/Util.java | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) 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 1c32e5e4..e2954f6a 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -989,7 +989,7 @@ public static boolean validateSign(final Document doc, final List getSignatureData(Node signNode, String alg) { return getSignatureData(signNode, alg, false); } - /** - * Validate signature (Metadata). + /** + * Extract signature data from a DOM {@link Node}. * - * @param doc - * The document we should validate - * @param cert - * The public certificate - * @param fingerprint - * The fingerprint of the public certificate + * @param signNode + * The signed node * @param alg * The signature algorithm method * @param rejectDeprecatedAlg - * Flag to invalidate or not Signatures with deprecated alg + * Whether to ignore signature if a deprecated algorithm is used * - * @return True if the sign is valid, false otherwise. + * @return a Map containing the signature data (actual signature, certificate, fingerprint) */ private static Map getSignatureData(Node signNode, String alg, Boolean rejectDeprecatedAlg) { Map signatureData = new HashMap<>(); From dae70f889730a76c4225dbde83939269a9885cc3 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 1 Apr 2021 17:03:16 +0200 Subject: [PATCH 055/133] Add Auth.getLastMessageIssueInstant and Auth.getLastRequestIssueInstant Each message (AuthnRequest, SamlResponse, LogoutRequest and LogoutResponse) have been enhanced to expose their issue instant. This is useful for logging purposes (i.e.: you want to track each request you generate and each response you receive, along with their issue instant), which is also required when implementing a SPID Service Provider (SPID is a SAML 2.0-based federated system used by the Italian government). I also tried to implement some tests for Auth, but I did not succeed for the "received message" scenario, because, to be coherent with the "last message id" case, the "last message issue instant" information is set on the Auth instance only if the message processing succeeds (i.e.: the processed message is valid). The test data used here seems to fail validation because of time expiration (which is reasonable), so testing these methods in the "receive" scenario would require to write test messages with valid timestamps dynamically. --- .../onelogin/saml2/authn/AuthnRequest.java | 9 +++ .../onelogin/saml2/authn/SamlResponse.java | 27 +++++++ .../saml2/exception/ValidationError.java | 1 + .../onelogin/saml2/logout/LogoutRequest.java | 77 +++++++++++++++---- .../onelogin/saml2/logout/LogoutResponse.java | 29 +++++++ .../saml2/test/authn/AuthnRequestTest.java | 30 ++++++++ .../saml2/test/authn/AuthnResponseTest.java | 24 ++++++ .../saml2/test/logout/LogoutRequestTest.java | 53 +++++++++++++ .../saml2/test/logout/LogoutResponseTest.java | 45 +++++++++++ .../main/java/com/onelogin/saml2/Auth.java | 35 +++++++++ .../com/onelogin/saml2/test/AuthTest.java | 6 ++ 11 files changed, 321 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index ba3ea21f..946d07d7 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -261,4 +261,13 @@ public String getId() { return id; } + + /** + * Returns the issue instant of this message. + * + * @return a new {@link Calendar} instance carrying the issue instant of this message + */ + public Calendar getIssueInstant() { + return issueInstant == null? null: (Calendar) issueInstant.clone(); + } } 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 7f38b81c..32f0edbd 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -4,6 +4,7 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -1239,4 +1240,30 @@ protected void validateSpNameQualifier(final String spNameQualifier) throws Vali throw new ValidationError("The SPNameQualifier value mismatch the SP entityID value.", ValidationError.SP_NAME_QUALIFIER_NAME_MISMATCH); } } + + /** + * Returns the issue instant of this message. + * + * @return a new {@link Calendar} instance carrying the issue instant of this message + * @throws ValidationError + * if the found IssueInstant attribute is not in the expected + * UTC form of ISO-8601 format + */ + public Calendar getResponseIssueInstant() throws ValidationError { + final Element rootElement = getSAMLResponseDocument() + .getDocumentElement(); + final String issueInstantString = rootElement.hasAttribute( + "IssueInstant")? rootElement.getAttribute("IssueInstant"): null; + if(issueInstantString == null) + return null; + final Calendar result = Calendar.getInstance(); + try { + result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + } catch (final IllegalArgumentException e) { + throw new ValidationError( + "The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format", + ValidationError.INVALID_ISSUE_INSTANT_FORMAT); + } + return result; + } } diff --git a/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java b/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java index 57ce5978..7af9fac7 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java +++ b/core/src/main/java/com/onelogin/saml2/exception/ValidationError.java @@ -53,6 +53,7 @@ public class ValidationError extends SAMLException { public static final int NOT_SUPPORTED = 46; public static final int KEY_ALGORITHM_ERROR = 47; public static final int MISSING_ENCRYPTED_ELEMENT = 48; + public static final int INVALID_ISSUE_INSTANT_FORMAT = 49; private int errorCode; 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 9caa9b39..c17b8e0f 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -144,7 +144,9 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, logoutRequestString = substitutor.replace(getLogoutRequestTemplate()); } else { logoutRequestString = Util.base64decodedInflated(samlLogoutRequest); - id = getId(logoutRequestString); + Document doc = Util.loadXML(logoutRequestString); + id = getId(doc); + issueInstant = getIssueInstant(doc); } } @@ -487,14 +489,14 @@ public Boolean isValid() throws Exception { } } - /** - * Returns the ID of the Logout Request Document. - * + /** + * Returns the ID of the Logout Request Document. + * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. * - * @return the ID of the Logout Request. - */ + * @return the ID of the Logout Request. + */ public static String getId(Document samlLogoutRequestDocument) { String id = null; try { @@ -505,19 +507,55 @@ public static String getId(Document samlLogoutRequestDocument) { return id; } - /** - * Returns the ID of the Logout Request String. - * - * @param samlLogoutRequestString - * A Logout Request string. - * - * @return the ID of the Logout Request. - * - */ + /** + * Returns the issue instant of the Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * + * @return the issue instant of the Logout Request. + */ + public static Calendar getIssueInstant(Document samlLogoutRequestDocument) { + Calendar issueInstant = null; + try { + Element rootElement = samlLogoutRequestDocument.getDocumentElement(); + rootElement.normalize(); + String issueInstantString = rootElement.hasAttribute( + "IssueInstant")? rootElement.getAttribute("IssueInstant"): null; + if(issueInstantString == null) + return null; + issueInstant = Calendar.getInstance(); + issueInstant.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + } catch (Exception e) {} + return issueInstant; + } + + /** + * Returns the ID of the Logout Request String. + * + * @param samlLogoutRequestString + * A Logout Request string. + * + * @return the ID of the Logout Request. + * + */ public static String getId(String samlLogoutRequestString) { Document doc = Util.loadXML(samlLogoutRequestString); return getId(doc); } + + /** + * Returns the issue instant of the Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * + * @return the issue instant of the Logout Request. + */ + public static Calendar getIssueInstant(String samlLogoutRequestString) { + Document doc = Util.loadXML(samlLogoutRequestString); + return getIssueInstant(doc); + } /** * Gets the NameID Data from the the Logout Request Document. @@ -766,4 +804,13 @@ public String getId() { return id; } + + /** + * Returns the issue instant of this message. + * + * @return a new {@link Calendar} instance carrying the issue instant of this message + */ + public Calendar getIssueInstant() { + return issueInstant == null? null: (Calendar) issueInstant.clone(); + } } 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 e943d826..9a8a6c97 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -475,4 +475,33 @@ public String getError() { public Exception getValidationException() { return validationException; } + + /** + * Returns the issue instant of this message. + * + * @return a new {@link Calendar} instance carrying the issue instant of this message + * @throws ValidationError + * if this logout response was received and parsed and the found IssueInstant + * attribute is not in the expected UTC form of ISO-8601 format + */ + public Calendar getIssueInstant() throws ValidationError { + if(logoutResponseDocument != null) { + final Element rootElement = logoutResponseDocument + .getDocumentElement(); + final String issueInstantString = rootElement.hasAttribute( + "IssueInstant")? rootElement.getAttribute("IssueInstant"): null; + if(issueInstantString == null) + return null; + final Calendar result = Calendar.getInstance(); + try { + result.setTimeInMillis(Util.parseDateTime(issueInstantString).getMillis()); + } catch (final IllegalArgumentException e) { + throw new ValidationError( + "The Response IssueInstant attribute is not in the expected UTC form of ISO-8601 format", + ValidationError.INVALID_ISSUE_INSTANT_FORMAT); + } + return result; + } else + return issueInstant == null? null: (Calendar) issueInstant.clone(); + } } diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index d08b5768..7b31a111 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -4,11 +4,15 @@ import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import org.junit.Assert; import org.junit.Test; import com.onelogin.saml2.authn.AuthnRequest; @@ -327,6 +331,32 @@ public void testGetId() throws Exception assertThat(authnRequestStr, containsString("ID=\"" + authnRequest.getId() + "\"")); } + /** + * Tests the getId method of AuthnRequest + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest.getId + */ + @Test + public void testGetIssueInstant() throws Exception + { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + final long start = System.currentTimeMillis(); + AuthnRequest authnRequest = new AuthnRequest(settings); + final long end = System.currentTimeMillis(); + final String authnRequestStr = Util.base64decodedInflated(authnRequest.getEncodedAuthnRequest()); + + final Calendar issueInstant = authnRequest.getIssueInstant(); + assertNotNull(issueInstant); + final long millis = issueInstant.getTimeInMillis(); + assertTrue(millis >= start && millis <= end); + + assertThat(authnRequestStr, containsString(" + * Case: LogoutRequest message built by the caller + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutRequest#getIssueInstant(String) + */ + @Test + public void testGetIssueInstantBuiltMessage() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + long start = System.currentTimeMillis(); + LogoutRequest logoutRequest = new LogoutRequest(settings, null); + long end = System.currentTimeMillis(); + Calendar issueInstant = logoutRequest.getIssueInstant(); + assertNotNull(issueInstant); + long millis = issueInstant.getTimeInMillis(); + assertTrue(millis >= start && millis <= end); + } + /** * Tests the getNameIdData method of LogoutRequest * Case: Not able to get the NameIdData due no private key to decrypt diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index fe04cb7c..7bb373b0 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -7,11 +7,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import com.onelogin.saml2.exception.ValidationError; import java.io.IOException; import java.net.URISyntaxException; +import java.util.Calendar; import javax.xml.xpath.XPathExpressionException; @@ -246,6 +248,49 @@ public void testGetIssuer() throws IOException, URISyntaxException, XMLEntityExc assertNull(logoutResponse.getIssuer()); } + /** + * Tests the getIssueInstant method of LogoutResponse + * + * @throws IOException + * @throws Error + * @throws ValidationError + * + * @see com.onelogin.saml2.logout.LogoutResponse#getIssueInstant() + */ + @Test + public void testGetIssueInstant() throws IOException, Error, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/logout_responses/logout_response_deflated.xml.base64"); + final String requestURL = "/"; + HttpRequest httpRequest = newHttpRequest(requestURL, samlResponseEncoded); + LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); + assertEquals("2013-12-10T04:39:31Z", Util.formatDateTime(logoutResponse.getIssueInstant().getTimeInMillis())); + } + + /** + * Tests the getIssueInstant method of LogoutResponse + *

+ * Case: LogoutResponse message built by the caller. + * + * @throws IOException + * @throws Error + * @throws ValidationError + * + * @see com.onelogin.saml2.logout.LogoutResponse#getIssueInstant() + */ + @Test + public void testGetIssueInstantBuiltMessage() throws IOException, Error, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + long start = System.currentTimeMillis(); + LogoutResponse logoutResponse = new LogoutResponse(settings, null); + logoutResponse.build(); + long end = System.currentTimeMillis(); + Calendar issueInstant = logoutResponse.getIssueInstant(); + assertNotNull(issueInstant); + long millis = issueInstant.getTimeInMillis(); + assertTrue(millis >= start && millis <= end); + } + /** * Tests the isValid method of LogoutResponse * Case: No SAML Logout Response diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 092f9324..5a6ac28b 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -6,6 +6,7 @@ import java.security.PrivateKey; import java.security.SignatureException; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; @@ -102,6 +103,11 @@ public class Auth { * The ID of the last message processed */ private String lastMessageId; + + /** + * The issue instant of the last message processed + */ + private Calendar lastMessageIssueInstant; /** * The ID of the last assertion processed @@ -143,6 +149,11 @@ public class Auth { */ private String lastRequestId; + /** + * The issue instant of the last request (Authn or Logout) generated + */ + private Calendar lastRequestIssueInstant; + /** * The most recently-constructed/processed XML SAML request * (AuthNRequest, LogoutRequest) @@ -392,6 +403,7 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool String ssoUrl = getSSOurl(); lastRequestId = authnRequest.getId(); + lastRequestIssueInstant = authnRequest.getIssueInstant(); lastRequest = authnRequest.getAuthnRequestXml(); if (!stay) { @@ -565,6 +577,7 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea String sloUrl = getSLOurl(); lastRequestId = logoutRequest.getId(); + lastRequestIssueInstant = logoutRequest.getIssueInstant(); lastRequest = logoutRequest.getLogoutRequestXml(); if (!stay) { @@ -814,6 +827,7 @@ public void processResponse(String requestId) throws Exception { sessionIndex = samlResponse.getSessionIndex(); sessionExpiration = samlResponse.getSessionNotOnOrAfter(); lastMessageId = samlResponse.getId(); + lastMessageIssueInstant = samlResponse.getResponseIssueInstant(); lastAssertionId = samlResponse.getAssertionId(); lastAssertionNotOnOrAfter = samlResponse.getAssertionNotOnOrAfter(); LOGGER.debug("processResponse success --> " + samlResponseParameter); @@ -894,6 +908,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } } else { lastMessageId = logoutResponse.getId(); + lastMessageIssueInstant = logoutResponse.getIssueInstant(); LOGGER.debug("processSLO success --> " + samlResponseParameter); if (!keepLocalSession) { request.getSession().invalidate(); @@ -913,6 +928,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta return null; } else { lastMessageId = logoutRequest.getId(); + lastMessageIssueInstant = logoutRequest.getIssueInstant(); LOGGER.debug("processSLO success --> " + samlRequestParameter); if (!keepLocalSession) { request.getSession().invalidate(); @@ -1059,6 +1075,15 @@ public final DateTime getSessionExpiration() { public String getLastMessageId() { return lastMessageId; } + + /** + * Returns the issue instant of the last message processed. + * + * @return The issue instant of the last message processed + */ + public Calendar getLastMessageIssueInstant() { + return lastMessageIssueInstant; + } /** * @return The ID of the last assertion processed @@ -1103,6 +1128,16 @@ public Exception getLastValidationException() { public String getLastRequestId() { return lastRequestId; } + + /** + * Returns the issue instant of the last request generated (AuthnRequest or LogoutRequest). + * + * @return the issue instant of the last request generated (AuthnRequest or LogoutRequest), + * null if none + */ + public Calendar getLastRequestIssueInstant() { + return lastRequestIssueInstant; + } /** * @return the Saml2Settings object. The Settings data. 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 12b62b68..7a1678b1 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -2088,6 +2088,9 @@ public void testGetLastAuthNRequest() throws IOException, SettingsException, Err String targetSSOURL = auth.login(null, false, false, false, true); String authNRequestXML = auth.getLastRequestXML(); assertThat(targetSSOURL, containsString(Util.urlEncoder(Util.deflatedBase64encoded(authNRequestXML)))); + + assertThat(authNRequestXML, containsString("ID=\"" + auth.getLastRequestId() + "\"")); + assertThat(authNRequestXML, containsString("IssueInstant=\"" + Util.formatDateTime(auth.getLastRequestIssueInstant().getTimeInMillis()) + "\"")); } /** @@ -2111,6 +2114,9 @@ public void testGetLastLogoutRequestSent() throws IOException, SettingsException String targetSLOURL = auth.logout(null, null, null, true); String logoutRequestXML = auth.getLastRequestXML(); assertThat(targetSLOURL, containsString(Util.urlEncoder(Util.deflatedBase64encoded(logoutRequestXML)))); + + assertThat(logoutRequestXML, containsString("ID=\"" + auth.getLastRequestId() + "\"")); + assertThat(logoutRequestXML, containsString("IssueInstant=\"" + Util.formatDateTime(auth.getLastRequestIssueInstant().getTimeInMillis()) + "\"")); } /** From 93049ecb5339bdae2eef27ec2f57ab95997b1ad5 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 7 Apr 2021 11:32:52 +0200 Subject: [PATCH 056/133] Provide a setter for validationException If a subclass needs to extend isValid(String), it must be able to set the validation exception. In fact, overriding any of the non-private validateXXX methods is trivial, because a ValidationError can simply be thrown, but if additional validation is required, which is not covered by any of those validateXXX methods, a subclass may need to override isValid(String) directly, and this does not throw ValidationError, but rather catches it so that it can be subsequently returned by getValidationException() to the consumer. Hence, a setter with at least protected visibility is needed for subclasses to do the same. --- .../java/com/onelogin/saml2/authn/SamlResponse.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 d3790f5a..3ec12efe 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -1022,6 +1022,17 @@ public String getError() { public Exception getValidationException() { return validationException; } + + /** + * Sets the validation exception that this {@link SamlResponse} should return + * when a validation error occurs. + * + * @param validationException + * the validation exception to set + */ + protected void setValidationException(Exception validationException) { + this.validationException = validationException; + } /** * Extracts a node from the DOMDocument (Assertion). From 1fba07dc4e25e72c15f1601110367550a77cd6b1 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 13 Apr 2021 17:46:40 +0200 Subject: [PATCH 057/133] Make the SamlResponse returned attribute map preserve attribute order The map returned by SamlResposne containing the attributes returned by the IdP now preserves the order in which such attributes appear in the SAML response XML. This is not strictly mandatory, but a plus. Indeed, the test method com.onelogin.saml2.test.AuthTest.testProcessResponse() was not deterministic before this change: indeed, the iteration order of HashMap is undetermined so expecting to see attribute names in a given order could lead to a test failure. This change also fixes this and attribute names are expected now to be seen in the order in which the corresponding attributes appear in the test XML file. --- .../src/main/java/com/onelogin/saml2/authn/SamlResponse.java | 3 ++- toolkit/src/main/java/com/onelogin/saml2/Auth.java | 2 +- toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) 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 7f38b81c..5a21b0e4 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -5,6 +5,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -572,7 +573,7 @@ public String getNameIdSPNameQualifier() throws Exception { * */ public HashMap> getAttributes() throws XPathExpressionException, ValidationError { - HashMap> attributes = new HashMap>(); + HashMap> attributes = new LinkedHashMap>(); NodeList nodes = this.queryAssertion("/saml:AttributeStatement/saml:Attribute"); diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 092f9324..767ab38f 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -116,7 +116,7 @@ public class Auth { /** * User attributes data. */ - private Map> attributes = new HashMap>(); + private Map> attributes = new LinkedHashMap>(); /** * If user is authenticated. 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 12b62b68..65a38d4e 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -31,6 +31,7 @@ import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -524,7 +525,7 @@ public void testProcessResponse() throws Exception { when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded})); Auth auth2 = new Auth(settings, request, response); - HashMap> expectedAttributes = new HashMap>(); + HashMap> expectedAttributes = new LinkedHashMap>(); List attrValues = new ArrayList(); attrValues.add("smartin"); List attrValues2 = new ArrayList(); @@ -538,9 +539,9 @@ public void testProcessResponse() throws Exception { attrValues5.add("Martin2"); expectedAttributes.put("uid", attrValues); expectedAttributes.put("mail", attrValues2); - expectedAttributes.put("eduPersonAffiliation", attrValues3); expectedAttributes.put("cn", attrValues4); expectedAttributes.put("sn", attrValues5); + expectedAttributes.put("eduPersonAffiliation", attrValues3); List keys = new ArrayList(expectedAttributes.keySet()); assertFalse(auth2.isAuthenticated()); From 3edc8993fe80dc5c88c697e1859608d3ea9fedca Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 14 Apr 2021 14:09:12 +0200 Subject: [PATCH 058/133] Ensure local resolution of schemas (and DTDs) Schemas in main/resources were not correctly used on schema loading when java-saml was used as a JAR in a consuming application. It seems like the local XSD files for imported schemas were used only when running unit tests, while remote HTTP lookups from the W3C website were made when using java-saml as a JAR. Now a LSResoureResolver is set on the schema factory so that any known XSD or DTD is loaded from the classpath, even when inside a JAR. Any other (unknown) schema is resolved in the standard way (and may involve a remote call). Also, in the unlikely event that retrieving the local copy of the XSD/DTD is impossible, a fallback mechanism ensures the standard resolution is performed. Please note that the online version of xenc-schema.xsd contains a reference to the XML Schema DTD. Now that we can resolve resources locally, I decided to keep the DTD and include it in /schemas package (along with the datatypes DTD). This should provide an even more comprehensive schema validation. Closes #327. --- .../onelogin/saml2/util/SchemaFactory.java | 82 +++- core/src/main/resources/schemas/XMLSchema.dtd | 402 ++++++++++++++++++ core/src/main/resources/schemas/datatypes.dtd | 203 +++++++++ .../main/resources/schemas/xenc-schema.xsd | 12 +- 4 files changed, 696 insertions(+), 3 deletions(-) create mode 100644 core/src/main/resources/schemas/XMLSchema.dtd create mode 100644 core/src/main/resources/schemas/datatypes.dtd diff --git a/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java b/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java index bc5e4044..647a0702 100644 --- a/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java +++ b/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java @@ -1,10 +1,18 @@ package com.onelogin.saml2.util; +import java.io.InputStream; import java.net.URL; +import java.util.Locale; import javax.xml.XMLConstants; import javax.xml.validation.Schema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.SAXException; /** @@ -14,6 +22,11 @@ */ public abstract class SchemaFactory { + /** + * Private property to construct a logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SchemaFactory.class); + private SchemaFactory() { //not called } @@ -24,7 +37,72 @@ private SchemaFactory() { .getResource("/schemas/saml-schema-protocol-2.0.xsd"); public static Schema loadFromUrl(URL schemaUrl) throws SAXException { - return javax.xml.validation.SchemaFactory - .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaUrl); + javax.xml.validation.SchemaFactory factory = javax.xml.validation.SchemaFactory + .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + factory.setResourceResolver(new LSResourceResolver() { + + private DOMImplementationLS ls; + + @Override + public LSInput resolveResource(final String type, final String namespaceURI, + final String publicId, final String systemId, final String baseURI) { + try { + if(namespaceURI != null) + switch (namespaceURI) { + case "urn:oasis:names:tc:SAML:2.0:assertion": + return getLocalResource("saml-schema-assertion-2.0.xsd"); + case "urn:oasis:names:tc:SAML:2.0:ac": + return getLocalResource("saml-schema-authn-context-2.0.xsd"); + case "urn:oasis:names:tc:SAML:2.0:metadata": + return getLocalResource("saml-schema-metadata-2.0.xsd"); + case "urn:oasis:names:tc:SAML:2.0:protocol": + return getLocalResource("saml-schema-protocol-2.0.xsd"); + case "urn:oasis:names:tc:SAML:metadata:attribute": + return getLocalResource("sstc-metadata-attr.xsd"); + case "urn:oasis:names:tc:SAML:attribute:ext": + return getLocalResource("sstc-saml-attribute-ext.xsd"); + case "urn:oasis:names:tc:SAML:metadata:algsupport": + return getLocalResource("sstc-saml-metadata-algsupport-v1.0.xsd"); + case "urn:oasis:names:tc:SAML:metadata:ui": + return getLocalResource("sstc-saml-metadata-ui-v1.0.xsd"); + case "http://www.w3.org/2001/04/xmlenc#": + return getLocalResource("xenc-schema.xsd"); + case "http://www.w3.org/XML/1998/namespace": + return getLocalResource("xml.xsd"); + case "http://www.w3.org/2000/09/xmldsig#": + return getLocalResource("xmldsig-core-schema.xsd"); + } + if("saml-schema-authn-context-types-2.0.xsd".equals(systemId)) + return getLocalResource("saml-schema-authn-context-types-2.0.xsd"); + if(publicId != null) + switch(publicId.toUpperCase(Locale.ROOT)) { + case "-//W3C//DTD XMLSCHEMA 200102//EN": + return getLocalResource("XMLSchema.dtd"); + case "DATATYPES": + return getLocalResource("datatypes.dtd"); + } + } catch (final Throwable e) { + // fallback to standard behaviour in case of errors + LOGGER.warn("could not resolve schema or DTD locally, proceeding the standard way", + e); + return null; + } + return null; + } + + public LSInput getLocalResource(String name) throws Throwable { + if (ls == null) { + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + ls = (DOMImplementationLS) registry.getDOMImplementation("LS 3.0"); + } + final InputStream inputStream = getClass().getResourceAsStream("/schemas/" + name); + if (inputStream == null) + return null; + final LSInput lsInput = ls.createLSInput(); + lsInput.setByteStream(inputStream); + return lsInput; + } + }); + return factory.newSchema(schemaUrl); } } diff --git a/core/src/main/resources/schemas/XMLSchema.dtd b/core/src/main/resources/schemas/XMLSchema.dtd new file mode 100644 index 00000000..e8e8f762 --- /dev/null +++ b/core/src/main/resources/schemas/XMLSchema.dtd @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%xs-datatypes; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/resources/schemas/datatypes.dtd b/core/src/main/resources/schemas/datatypes.dtd new file mode 100644 index 00000000..8e48553b --- /dev/null +++ b/core/src/main/resources/schemas/datatypes.dtd @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/resources/schemas/xenc-schema.xsd b/core/src/main/resources/schemas/xenc-schema.xsd index d6d79103..85af68b5 100644 --- a/core/src/main/resources/schemas/xenc-schema.xsd +++ b/core/src/main/resources/schemas/xenc-schema.xsd @@ -1,4 +1,14 @@ + + + + + ]> + schemaLocation='http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd'/> From 119f7fe310029f900fe38f34c42295b9d5474ea5 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 14 Apr 2021 14:53:45 +0200 Subject: [PATCH 059/133] Fix extraction of the response issuer A ">1" in place of a ">0" was causing the inability to extract the Response issuer(s) correctly. Added a test case to check this. Fixed another test case that should (IMHO) return an error regarding the issuer, rather than the signature. --- .../onelogin/saml2/authn/SamlResponse.java | 2 +- .../saml2/test/authn/AuthnResponseTest.java | 29 +++++++++++++++++-- .../invalids/different_issuers.xml.base64 | 1 + 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 core/src/test/resources/data/responses/invalids/different_issuers.xml.base64 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 7f38b81c..4e6900c3 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -717,7 +717,7 @@ public List getIssuers() throws XPathExpressionException, ValidationErro List issuers = new ArrayList(); String value; NodeList responseIssuer = Util.query(samlResponseDocument, "/samlp:Response/saml:Issuer"); - if (responseIssuer.getLength() > 1) { + if (responseIssuer.getLength() > 0) { if (responseIssuer.getLength() == 1) { value = responseIssuer.item(0).getTextContent(); if (!issuers.contains(value)) { 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 cae24ce2..474b14ca 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 @@ -875,6 +875,32 @@ public void testGetIssuers() throws IOException, Error, XPathExpressionException assertEquals(expectedIssuers, samlResponse.getIssuers()); } + /** + * Tests the getIssuers method of SamlResponse + *

+ * Case: different issuers for response and assertion + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetIssuersDifferentIssuers() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/different_issuers.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + List expectedIssuers = new ArrayList(); + expectedIssuers.add("https://response-issuer.com"); + expectedIssuers.add("https://assertion-issuer.com"); + assertEquals(expectedIssuers, samlResponse.getIssuers()); + } + /** * Tests the getIssuers method of SamlResponse * Case: Issuer of the assertion not found @@ -1787,8 +1813,7 @@ public void testIsInValidIssuer() throws IOException, Error, XPathExpressionExce settings.setStrict(true); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertFalse(samlResponse.isValid()); - assertEquals("No Signature found. SAML Response rejected", samlResponse.getError()); - + assertEquals("Invalid issuer in the Assertion/Response. Was 'http://invalid.isser.example.com/', but expected 'http://idp.example.com/'", samlResponse.getError()); } /** diff --git a/core/src/test/resources/data/responses/invalids/different_issuers.xml.base64 b/core/src/test/resources/data/responses/invalids/different_issuers.xml.base64 new file mode 100644 index 00000000..299eda8e --- /dev/null +++ b/core/src/test/resources/data/responses/invalids/different_issuers.xml.base64 @@ -0,0 +1 @@ +DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9IkdPU0FNTFIxMjkwMTE3NDU3MTc5NCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTAtMTEtMThUMjE6NTc6MzdaIiBEZXN0aW5hdGlvbj0ie3JlY2lwaWVudH0iPg0KICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly9yZXNwb25zZS1pc3N1ZXIuY29tPC9zYW1sOklzc3Vlcj4NCiAgPHNhbWxwOlN0YXR1cz4NCiAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+DQogIDxzYW1sOkFzc2VydGlvbiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIFZlcnNpb249IjIuMCIgSUQ9InBmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiIgSXNzdWVJbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiI+DQogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXNzZXJ0aW9uLWlzc3Vlci5jb208L3NhbWw6SXNzdWVyPg0KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICAgICAgPGRzOlNpZ25lZEluZm8+DQogICAgICAgIDxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgICAgICAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhhNDY1NzRkZi1iM2IwLWEwNmEtMjNjOC02MzY0MTMxOTg3NzIiPg0KICAgICAgICAgIDxkczpUcmFuc2Zvcm1zPg0KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+DQogICAgICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+DQogICAgICAgICAgPC9kczpUcmFuc2Zvcm1zPg0KICAgICAgICAgIDxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPg0KICAgICAgICAgIDxkczpEaWdlc3RWYWx1ZT5wSlE3TVMvZWs0S1JSV0dtdi9INDNSZUhZTXM9PC9kczpEaWdlc3RWYWx1ZT4NCiAgICAgICAgPC9kczpSZWZlcmVuY2U+DQogICAgICA8L2RzOlNpZ25lZEluZm8+DQogICAgICA8ZHM6U2lnbmF0dXJlVmFsdWU+eWl2ZUtjUGREcHVETmo2c2hyUTNBQndyL2NBM0NyeUQycGhHL3hMWnN6S1d4VTUvbWxhS3Q4ZXdiWk9kS0t2dE9zMnBIQnk1RHVhM2s5NEFGK3p4R3llbDVnT293bW95WEpyK0FPcitrUE8wdmxpMVY4bzNoUFBVWndSZ1NYNlE5cFMxQ3FRZ2hLaUVhc1J5eWxxcUpVYVBZem1Pek9FOC9YbE1rd2lXbU8wPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQogICAgICA8ZHM6S2V5SW5mbz4NCiAgICAgICAgPGRzOlg1MDlEYXRhPg0KICAgICAgICAgIDxkczpYNTA5Q2VydGlmaWNhdGU+TUlJQnJUQ0NBYUdnQXdJQkFnSUJBVEFEQmdFQU1HY3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1saE1SVXdFd1lEVlFRSERBeFRZVzUwWVNCTmIyNXBZMkV4RVRBUEJnTlZCQW9NQ0U5dVpVeHZaMmx1TVJrd0Z3WURWUVFEREJCaGNIQXViMjVsYkc5bmFXNHVZMjl0TUI0WERURXdNRE13T1RBNU5UZzBOVm9YRFRFMU1ETXdPVEE1TlRnME5Wb3daekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhGVEFUQmdOVkJBY01ERk5oYm5SaElFMXZibWxqWVRFUk1BOEdBMVVFQ2d3SVQyNWxURzluYVc0eEdUQVhCZ05WQkFNTUVHRndjQzV2Ym1Wc2IyZHBiaTVqYjIwd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFPalN1MWZqUHk4ZDV3NFF5TDEremQ0aEl3MU1ra2ZmNFdZL1RMRzhPWmtVNVlUU1dtbUhQRDVrdllINXVvWFMvNnFRODFxWHBSMndWOENUb3daSlVMZzA5ZGRSZFJuOFFzcWoxRnlPQzVzbEUzeTJiWjJvRnVhNzJvZi80OWZwdWpuRlQ2S25RNjFDQk1xbERvVFFxT1Q2MnZHSjhuUDZNWld2QTZzeHF1ZDVBZ01CQUFFd0F3WUJBQU1CQUE9PTwvZHM6WDUwOUNlcnRpZmljYXRlPg0KICAgICAgICA8L2RzOlg1MDlEYXRhPg0KICAgICAgPC9kczpLZXlJbmZvPg0KICAgIDwvZHM6U2lnbmF0dXJlPg0KICAgIDxzYW1sOlN1YmplY3Q+DQogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnN1cHBvcnRAb25lbG9naW4uY29tPC9zYW1sOk5hbWVJRD4NCiAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4NCiAgICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDEwLTExLTE4VDIyOjAyOjM3WiIgUmVjaXBpZW50PSJ7cmVjaXBpZW50fSIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPg0KICAgIDwvc2FtbDpTdWJqZWN0Pg0KICAgIDxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDEwLTExLTE4VDIxOjUyOjM3WiIgTm90T25PckFmdGVyPSIyMDEwLTExLTE4VDIyOjAyOjM3WiI+DQogICAgICA8c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPg0KICAgICAgICA8c2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPg0KICAgICAgPC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+DQogICAgPC9zYW1sOkNvbmRpdGlvbnM+DQogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj4NCiAgICAgIDxzYW1sOkF1dGhuQ29udGV4dD4NCiAgICAgICAgPHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+DQogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0Pg0KICAgIDwvc2FtbDpBdXRoblN0YXRlbWVudD4NCiAgICA8c2FtbDpBdHRyaWJ1dGVTdGF0ZW1lbnQ+DQogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5kZW1vPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPg0KICAgICAgPC9zYW1sOkF0dHJpYnV0ZT4NCiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj4NCiAgICAgICAgPHNhbWw6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj52YWx1ZTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT4NCiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+DQogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4NCiAgPC9zYW1sOkFzc2VydGlvbj4NCjwvc2FtbHA6UmVzcG9uc2U+DQo= \ No newline at end of file From 7692f563cc15502a39a233544dca648d12a6deae Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 15 Apr 2021 10:53:58 +0200 Subject: [PATCH 060/133] Split Response and Assertion Issuer retrieval The SamlResponse.getIssuers() contract is quite controversial. For a valid response, it will always return just one element. For an invalid response, depending on the cause it may: - fail if no Assertion is present: this means, in particular, that if the status code is not a success one, it's impossible to retrieve the Response issuer with this method (although it may be a reasonable requirement, for logging purposes for instance) - fail if multiple Assertions are present: again, the Response Issuer cannot be retrieved in this case either - fail in the unlikely event that multiple Response Issuers were found - return up to 2 issuers at most, if different issuers were set on the Response and on the Assertion (which will make isValid() fail), with the inability to determine which is the Response Issuer and which is the Assertion Issuer (indeed: the former will be the first one in the list, the latter will be the second, but this contract is a bit weak) For these reasons, two different methods were provided to retrieve the Response and the Assertion Issuers, with the former that will succeed even when the status code is not a successful one. Also, because of the above reasons, the getIssuers() method was deprecated in favour of the two new ones. --- .../onelogin/saml2/authn/SamlResponse.java | 81 +++++++++++++++---- .../saml2/test/authn/AuthnResponseTest.java | 71 +++++++++++++--- 2 files changed, 125 insertions(+), 27 deletions(-) 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 4e6900c3..7753cf99 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -706,38 +706,89 @@ public List getAudiences() throws XPathExpressionException { } /** - * Gets the Issuers (from Response and Assertion). + * Gets the Response Issuer. * - * @return the issuers of the assertion/response + * @return the Response Issuer, or null if not specified * * @throws XPathExpressionException * @throws ValidationError + * if multiple Response issuers were found + * @see #getAssertionIssuer() + * @see #getIssuers() */ - public List getIssuers() throws XPathExpressionException, ValidationError { - List issuers = new ArrayList(); - String value; + public String getResponseIssuer() throws XPathExpressionException, ValidationError { NodeList responseIssuer = Util.query(samlResponseDocument, "/samlp:Response/saml:Issuer"); if (responseIssuer.getLength() > 0) { if (responseIssuer.getLength() == 1) { - value = responseIssuer.item(0).getTextContent(); - if (!issuers.contains(value)) { - issuers.add(value); - } + return responseIssuer.item(0).getTextContent(); } else { throw new ValidationError("Issuer of the Response is multiple.", ValidationError.ISSUER_MULTIPLE_IN_RESPONSE); } } - + return null; + } + + /** + * Gets the Assertion Issuer. + * + * @return the Assertion Issuer + * + * @throws XPathExpressionException + * @throws ValidationError + * if no Assertion Issuer could be found, or if multiple Assertion + * issuers were found + * @see #getResponseIssuer() + * @see #getIssuers() + */ + public String getAssertionIssuer() throws XPathExpressionException, ValidationError { NodeList assertionIssuer = this.queryAssertion("/saml:Issuer"); if (assertionIssuer.getLength() == 1) { - value = assertionIssuer.item(0).getTextContent(); - if (!issuers.contains(value)) { - issuers.add(value); - } + return assertionIssuer.item(0).getTextContent(); } else { throw new ValidationError("Issuer of the Assertion not found or multiple.", ValidationError.ISSUER_NOT_FOUND_IN_ASSERTION); } - + } + + /** + * Gets the Issuers (from Response and Assertion). If the same issuer appears + * both in the Response and in the Assertion (as it should), the returned list + * will contain it just once. Hence, the returned list should always return one + * element and in particular: + *

    + *
  • it will never contain zero elements (it means an Assertion Issuer could + * not be found, hence a {@link ValidationError} will be thrown instead) + *
  • if it contains more than one element, it means that the response is + * invalid and one of the returned issuers won't pass the check performed by + * {@link #isValid(String)} (which requires both issuers to be equal to the + * Identity Provider entity id) + *
+ *

+ * Warning: as a consequence of the above, if this response status code is not a + * successful one, this method will throw a {@link ValidationError} because it + * won't find any Assertion Issuer. In this case, if you need to retrieve the + * Response Issuer any way, you must use {@link #getResponseIssuer()} instead. + * + * @return the issuers of the assertion/response + * + * @throws XPathExpressionException + * @throws ValidationError + * if multiple Response Issuers or multiple Assertion Issuers were + * found, or if no Assertion Issuer could be found + * @see #getResponseIssuer() + * @see #getAssertionIssuer() + * @deprecated use {@link #getResponseIssuer()} and/or + * {@link #getAssertionIssuer()}; the contract of this method is + * quite controversial + */ + @Deprecated + public List getIssuers() throws XPathExpressionException, ValidationError { + List issuers = new ArrayList(); + String responseIssuer = getResponseIssuer(); + if(responseIssuer != null) + issuers.add(responseIssuer); + String assertionIssuer = getAssertionIssuer(); + if(!issuers.contains(assertionIssuer)) + issuers.add(assertionIssuer); return issuers; } 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 474b14ca..fe2169bc 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 @@ -822,7 +822,7 @@ public void testGetAudiences() throws IOException, Error, XPathExpressionExcepti } /** - * Tests the getIssuers method of SamlResponse + * Tests the getIssuers methods of SamlResponse * * @throws Error * @throws IOException @@ -837,46 +837,61 @@ public void testGetAudiences() throws IOException, Error, XPathExpressionExcepti @Test public void testGetIssuers() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); - String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64"); + String samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + String expectedIssuer = "http://idp.example.com/"; List expectedIssuers = new ArrayList(); - expectedIssuers.add("http://idp.example.com/"); - samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64"); - samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + expectedIssuers.add(expectedIssuer); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); expectedIssuers.remove(0); - expectedIssuers.add("https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"); + expectedIssuer = "https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php"; + expectedIssuers.add(expectedIssuer); samlResponseEncoded = Util.getFileAsString("data/responses/signed_message_encrypted_assertion.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); samlResponseEncoded = Util.getFileAsString("data/responses/double_signed_encrypted_assertion.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); samlResponseEncoded = Util.getFileAsString("data/responses/signed_encrypted_assertion.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); samlResponseEncoded = Util.getFileAsString("data/responses/double_signed_response.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); samlResponseEncoded = Util.getFileAsString("data/responses/signed_assertion_response.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); + expectedIssuer = "https://app.onelogin.com/saml/metadata/13590"; expectedIssuers = new ArrayList(); - expectedIssuers.add("https://app.onelogin.com/saml/metadata/13590"); + expectedIssuers.add(expectedIssuer); samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_response.xml.base64"); samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertNull(expectedIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); } /** - * Tests the getIssuers method of SamlResponse + * Tests the getIssuers methods of SamlResponse *

* Case: different issuers for response and assertion * @@ -896,13 +911,44 @@ public void testGetIssuersDifferentIssuers() throws IOException, Error, XPathExp String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/different_issuers.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); List expectedIssuers = new ArrayList(); - expectedIssuers.add("https://response-issuer.com"); - expectedIssuers.add("https://assertion-issuer.com"); + String expectedResponseIssuer = "https://response-issuer.com"; + String expectedAssertionIssuer = "https://assertion-issuer.com"; + expectedIssuers.add(expectedResponseIssuer); + expectedIssuers.add(expectedAssertionIssuer); + assertEquals(expectedResponseIssuer, samlResponse.getResponseIssuer()); + assertEquals(expectedAssertionIssuer, samlResponse.getAssertionIssuer()); assertEquals(expectedIssuers, samlResponse.getIssuers()); } /** - * Tests the getIssuers method of SamlResponse + * Tests the getAssertionIssuer method of SamlResponse + *

+ * Case: Issuer of the assertion not found + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetAssertionIssuerNoInAssertion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_assertion.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + + expectedEx.expect(ValidationError.class); + expectedEx.expectMessage("Issuer of the Assertion not found or multiple."); + samlResponse.getAssertionIssuer(); + } + + /** + * Tests the getIssuers methods of SamlResponse + *

* Case: Issuer of the assertion not found * * @throws Error @@ -921,11 +967,12 @@ public void testGetIssuersNoInAssertion() throws IOException, Error, XPathExpres String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_assertion.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + samlResponse.getResponseIssuer(); // this should not fail expectedEx.expect(ValidationError.class); expectedEx.expectMessage("Issuer of the Assertion not found or multiple."); samlResponse.getIssuers(); } - + /** * Tests the getSessionIndex method of SamlResponse * From fea12c5bf40839561be3e2f0b69b14c8bd75604c Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 16 Apr 2021 17:10:15 +0200 Subject: [PATCH 061/133] Remove useless XMLEntityException declaration in logout throws clause This exception is indeed never thrown and it's a checked one, so it makes using Auth.logout heavier then necessary. java-saml already has an abundance of checked exceptions to handle, some of which could easily be translated into unchecked ones or caught and recovered cleanly, this is why I think that, if a useless exception declaration is found, it should better be removed. This indeed is a small source-incompatible change as soon as the consumer code is catching XMLEntityException explicitly (rather than SAMLException, Exception or Throwable). However, fixing this is trivial, just remove XMLEntityException from the catch clause (which will never be triggered anyway). --- .../onelogin/saml2/logout/LogoutRequest.java | 26 ++++---------- .../main/java/com/onelogin/saml2/Auth.java | 34 ++++++------------- 2 files changed, 18 insertions(+), 42 deletions(-) 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 9caa9b39..4ef242b3 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -117,10 +117,8 @@ public class LogoutRequest { * The NameID NameQualifier that will be set in the LogoutRequest. * @param nameIdSPNameQualifier * The SP Name Qualifier that will be set in the LogoutRequest. - * - * @throws XMLEntityException */ - public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) { this.settings = settings; this.request = request; @@ -163,10 +161,8 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The nameIdFormat that will be set in the LogoutRequest. * @param nameIdNameQualifier * The NameID NameQualifier will be set in the LogoutRequest. - * - * @throws XMLEntityException */ - public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier) { this(settings, request, nameId, sessionIndex, nameIdFormat, nameIdNameQualifier, null); } @@ -183,10 +179,8 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The SessionIndex (taken from the SAML Response in the SSO process). * @param nameIdFormat * The nameIdFormat that will be set in the LogoutRequest. - * - * @throws XMLEntityException */ - public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat) { this(settings, request, nameId, sessionIndex, nameIdFormat, null); } @@ -201,10 +195,8 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The NameID that will be set in the LogoutRequest. * @param sessionIndex * The SessionIndex (taken from the SAML Response in the SSO process). - * - * @throws XMLEntityException */ - public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex) { this(settings, request, nameId, sessionIndex, null); } @@ -213,10 +205,8 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * * @param settings * OneLogin_Saml2_Settings - * - * @throws XMLEntityException */ - public LogoutRequest(Saml2Settings settings) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings) { this(settings, null, null, null); } @@ -226,11 +216,9 @@ public LogoutRequest(Saml2Settings settings) throws XMLEntityException { * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). - * - * @throws XMLEntityException + * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). */ - public LogoutRequest(Saml2Settings settings, HttpRequest request) throws XMLEntityException { + public LogoutRequest(Saml2Settings settings, HttpRequest request) { this(settings, request, null, null); } diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 092f9324..af113824 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -25,7 +25,6 @@ import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.Error; -import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutResponse; @@ -493,12 +492,11 @@ public void login(String returnTo) throws IOException, SettingsException { * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { Map parameters = new HashMap(); return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier, parameters); @@ -528,12 +526,11 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier, Map parameters) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { if (parameters == null) { parameters = new HashMap(); @@ -593,11 +590,10 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat, - String nameIdNameQualifier) throws IOException, XMLEntityException, SettingsException { + String nameIdNameQualifier) throws IOException, SettingsException { return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, null); } @@ -618,11 +614,10 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { return logout(returnTo, nameId, sessionIndex, stay, nameidFormat, null); } @@ -642,11 +637,10 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { return logout(returnTo, nameId, sessionIndex, stay, null); } @@ -668,12 +662,11 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in * the LogoutRequest. * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { logout(returnTo, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier); } @@ -693,11 +686,10 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * LogoutRequest. * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat, - String nameIdNameQualifier) throws IOException, XMLEntityException, SettingsException { + String nameIdNameQualifier) throws IOException, SettingsException { logout(returnTo, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier); } @@ -713,11 +705,10 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * process). * @param nameidFormat The NameID Format will be set in the LogoutRequest. * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { logout(returnTo, nameId, sessionIndex, false, nameidFormat); } @@ -733,11 +724,10 @@ public void logout(String returnTo, String nameId, String sessionIndex, String n * process). * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ public void logout(String returnTo, String nameId, String sessionIndex) - throws IOException, XMLEntityException, SettingsException { + throws IOException, SettingsException { logout(returnTo, nameId, sessionIndex, false, null); } @@ -745,10 +735,9 @@ public void logout(String returnTo, String nameId, String sessionIndex) * Initiates the SLO process. * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ - public void logout() throws IOException, XMLEntityException, SettingsException { + public void logout() throws IOException, SettingsException { logout(null, null, null, false); } @@ -760,10 +749,9 @@ public void logout() throws IOException, XMLEntityException, SettingsException { * appended at all when an empty string is provided * * @throws IOException - * @throws XMLEntityException * @throws SettingsException */ - public void logout(String returnTo) throws IOException, XMLEntityException, SettingsException { + public void logout(String returnTo) throws IOException, SettingsException { logout(returnTo, null, null); } From f4059bf58b7b03ba39da134e79517e2e966b367f Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Mon, 19 Apr 2021 12:07:19 +0200 Subject: [PATCH 062/133] Remove the useless Exception throws declaration in LogoutRequest.isValid Two reasons for removal: - avoid the need of a useless try/catch on consuming code - align API to LogoutResponse.isValid() and SamlResponse.isValid() The code is backward compatible, because an existing surrounding try/catch(Exception) won't cause a compilation error after the change. --- .../com/onelogin/saml2/logout/LogoutRequest.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) 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 9caa9b39..944b8cab 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -358,14 +358,12 @@ private static StringBuilder getLogoutRequestTemplate() { return template; } - /** - * Determines if the SAML LogoutRequest is valid or not - * - * @return true if the SAML LogoutRequest is valid - * - * @throws Exception - */ - public Boolean isValid() throws Exception { + /** + * Determines if the SAML LogoutRequest is valid or not + * + * @return true if the SAML LogoutRequest is valid + */ + public Boolean isValid() { validationException = null; try { From 12683e6d5b802e0aafe6804a04f47c512cf2f81e Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Mon, 19 Apr 2021 14:07:32 +0200 Subject: [PATCH 063/133] Make LogoutRequest and LogoutResponse more extensible This increases the visibility of LogoutResponse.query() to protected and adds setValidationException to both LogoutResponse and LogoutRequest to ease the development of extension classes that need to customize the validation process. --- .../onelogin/saml2/logout/LogoutRequest.java | 10 +++++++ .../onelogin/saml2/logout/LogoutResponse.java | 28 +++++++++++++------ 2 files changed, 30 insertions(+), 8 deletions(-) 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 9caa9b39..ab85b668 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -758,6 +758,16 @@ public Exception getValidationException() { return validationException; } + /** + * Sets the validation exception that this {@link LogoutRequest} should return + * when a validation error occurs. + * + * @param validationException + * the validation exception to set + */ + protected void setValidationException(Exception validationException) { + this.validationException = validationException; + } /** * @return the ID of the Logout Request 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 e943d826..a05e2a30 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -338,14 +338,15 @@ public SamlResponseStatus getSamlResponseStatus() throws ValidationError } /** - * Extracts nodes that match the query from the DOMDocument (Logout Response Menssage) - * - * @param query - * Xpath Expression - * - * @return DOMNodeList The queried nodes - */ - private NodeList query (String query) throws XPathExpressionException { + * Extracts nodes that match the query from the DOMDocument (Logout Response Menssage) + * + * @param query + * Xpath Expression + * + * @return DOMNodeList The queried nodes + * @throws XPathExpressionException + */ + protected NodeList query (String query) throws XPathExpressionException { return Util.query(this.logoutResponseDocument, query, null); } @@ -475,4 +476,15 @@ public String getError() { public Exception getValidationException() { return validationException; } + + /** + * Sets the validation exception that this {@link LogoutResponse} should return + * when a validation error occurs. + * + * @param validationException + * the validation exception to set + */ + protected void setValidationException(Exception validationException) { + this.validationException = validationException; + } } From e4ebfe4091a245bcf004562954cc54aeac8a7cff Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 19:52:18 +0200 Subject: [PATCH 064/133] Create maven.yml --- .github/workflows/maven.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..0327c161 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,25 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: java-saml CI with Maven + +on: [push, pull_request] + +jobs: + test: + + runs-on: runs-on: ${{ matrix.os }} + strategy: + matrix: + java: [ '7', '8', '9', '10', '11' ] + os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] + name: Java ${{ matrix.Java }} (${{ matrix.os }}) + steps: + - uses: actions/checkout@v2 + - name: Set up Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: ${{ matrix.java }} + - name: Maven Test + run: mvn --batch-mode clean verify org.jacoco:jacoco-maven-plugin:report From 03e726a6547bb146aa69142f22e8633d6c7b3620 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 19:57:24 +0200 Subject: [PATCH 065/133] Update maven.yml --- .github/workflows/maven.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 0327c161..1a0849e3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,11 +8,11 @@ on: [push, pull_request] jobs: test: - runs-on: runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: - matrix: - java: [ '7', '8', '9', '10', '11' ] - os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] + matrix: + java: [ '7', '8', '9', '10', '11' ] + os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] name: Java ${{ matrix.Java }} (${{ matrix.os }}) steps: - uses: actions/checkout@v2 From 9966e743febf502e2666f143e4f838054b38123f Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 19:58:13 +0200 Subject: [PATCH 066/133] Update maven.yml --- .github/workflows/maven.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1a0849e3..eb7082e4 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -9,10 +9,10 @@ jobs: test: runs-on: ${{ matrix.os }} - strategy: - matrix: - java: [ '7', '8', '9', '10', '11' ] - os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] + strategy: + matrix: + java: [ '7', '8', '9', '10', '11' ] + os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] name: Java ${{ matrix.Java }} (${{ matrix.os }}) steps: - uses: actions/checkout@v2 From 665d618086788992ab227a8f057ff7dc9a9a48df Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 20:19:17 +0200 Subject: [PATCH 067/133] Update maven.yml --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index eb7082e4..40886edf 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -11,7 +11,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - java: [ '7', '8', '9', '10', '11' ] + java: [ '8', '9', '10', '11' ] os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] name: Java ${{ matrix.Java }} (${{ matrix.os }}) steps: From 87310e9a8ff4731317dfae98fdebcf14380c2ed6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 14 May 2021 20:22:22 +0200 Subject: [PATCH 068/133] Update maven.yml --- .github/workflows/maven.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 40886edf..089333c0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,9 +10,10 @@ jobs: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: java: [ '8', '9', '10', '11' ] - os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] + os: [ 'ubuntu-latest', 'windows-latest' ] name: Java ${{ matrix.Java }} (${{ matrix.os }}) steps: - uses: actions/checkout@v2 From d006fb8b77b550af285c27bfd14c46e9db7e8b39 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 15 May 2021 03:20:57 +0200 Subject: [PATCH 069/133] Update dependencies --- core/pom.xml | 6 +++--- toolkit/pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 6a57a920..3857c7a5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -60,7 +60,7 @@ org.apache.santuario xmlsec - 2.2.0 + 2.2.2 commons-codec @@ -72,13 +72,13 @@ com.azure azure-security-keyvault-keys - 4.2.1 + 4.2.8 true com.azure azure-identity - 1.0.9 + 1.3.0 true diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 43f79774..591b3379 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -81,7 +81,7 @@ org.apache.santuario xmlsec - 2.2.0 + 2.2.2 commons-codec From 3c79c8c80adadfb10457bec4c06f3b6f3fe26221 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sat, 15 May 2021 04:17:20 +0200 Subject: [PATCH 070/133] Fix CI --- .nvd-suppressions.xml | 49 +++++++++++++++++++++++++++++++++++++++++++ pom.xml | 3 ++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/.nvd-suppressions.xml b/.nvd-suppressions.xml index 3af0cdfa..04234612 100644 --- a/.nvd-suppressions.xml +++ b/.nvd-suppressions.xml @@ -1,3 +1,52 @@ + + + dd91fb744c2ff921407475cb29a1e3fee397d411 + CVE-2017-1000190 + + + + 6ce200f6b23222af3d8abb6b6459e6c44f4bb0e9 + CVE-2018-10237 + + + + 6ce200f6b23222af3d8abb6b6459e6c44f4bb0e9 + CVE-2020-8908 + + + + 7e82e3c4c593f85addf4bd209abde4f8ff933a07 + CVE-2020-29242 + + + + 7e82e3c4c593f85addf4bd209abde4f8ff933a07 + CVE-2020-29243 + + + + 7e82e3c4c593f85addf4bd209abde4f8ff933a07 + CVE-2020-29244 + + + + 7e82e3c4c593f85addf4bd209abde4f8ff933a07 + CVE-2020-29245 + diff --git a/pom.xml b/pom.xml index 813e7631..7b8d16a2 100644 --- a/pom.xml +++ b/pom.xml @@ -96,12 +96,13 @@ org.owasp dependency-check-maven - 6.0.3 + 6.1.6 7 .nvd-suppressions.xml + false From e2ca369d8625b0458e847d446bb4190f0214db4a Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 18 May 2021 02:46:59 +0200 Subject: [PATCH 071/133] Restrict CI integration for ubuntu and java 8 and 11 for now --- .github/workflows/maven.yml | 4 ++-- core/pom.xml | 5 ++++- pom.xml | 1 + toolkit/pom.xml | 5 ++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 089333c0..88ce2a45 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -12,8 +12,8 @@ jobs: strategy: fail-fast: false matrix: - java: [ '8', '9', '10', '11' ] - os: [ 'ubuntu-latest', 'windows-latest' ] + java: [ '8', '11' ] + os: [ 'ubuntu-latest' ] name: Java ${{ matrix.Java }} (${{ matrix.os }}) steps: - uses: actions/checkout@v2 diff --git a/core/pom.xml b/core/pom.xml index 3857c7a5..77087d29 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -118,7 +118,10 @@ maven-surefire-plugin 2.22.2 - ${jacoco.agent.argLine} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${jacoco.agent.argLine} -Dfile.encoding=${project.build.sourceEncoding} -Dline.separator=\n diff --git a/pom.xml b/pom.xml index 7b8d16a2..68340bef 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ UTF-8 + UTF-8 1.7.30 4.13.1 1.2.3 diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 591b3379..14dd363e 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -113,7 +113,10 @@ maven-surefire-plugin 2.22.2 - ${jacoco.agent.argLine} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${jacoco.agent.argLine} -Dfile.encoding=${project.build.sourceEncoding} -Dline.separator=\n From f96a229d1680ef491a0874fbb4d97a054266f94f Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Wed, 26 May 2021 16:39:31 +0200 Subject: [PATCH 072/133] Add warning about the use of IdPMetadataParser class --- README.md | 7 +++++++ .../com/onelogin/saml2/settings/IdPMetadataParser.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/README.md b/README.md index 14d82177..60ddcc7b 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,13 @@ In production, the **onelogin.saml2.strict** setting parameter MUST be set as ** In production also we highly recommend to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment. +The IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. + +Usually the same administrator that handles the Service Provider also sets the URL to the IdP, which should be a trusted resource. + +But there are other scenarios, like a SAAS app where the administrator of the app delegates this functionality to other users. In this case, extra precaution should be taken in order to validate such URL inputs and avoid attacks like SSRF. + + ## Installation ### Hosting #### Github diff --git a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java index 197d7476..d2d46426 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java +++ b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java @@ -24,6 +24,8 @@ * * A class that implements the settings parser from IdP Metadata * + * This class does not validate in any way the URL that is introduced, + * make sure to validate it properly before use it in a get_metadata method. */ public class IdPMetadataParser { From c4907d4a4d928ceb1dbeda82ec9bfbb3d7cb2760 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 16 Apr 2021 15:41:22 +0200 Subject: [PATCH 073/133] Trim the extracted subject name id If the subject name id is not trimmed, its value may contain surrounding whitespace characters depending on XML formatting. This change also avoids a double trim of audiences (which indeed were already trimmed). --- .../java/com/onelogin/saml2/authn/SamlResponse.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 b3022acb..674a1680 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -14,6 +14,8 @@ import javax.xml.xpath.XPathExpressionException; 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; @@ -469,7 +471,10 @@ public Map getNameIdData() throws Exception { if (nameIdElem != null) { String value = nameIdElem.getTextContent(); - if (settings.isStrict() && value.isEmpty()) { + if(value != null) { + value = value.trim(); + } + if (settings.isStrict() && StringUtils.isEmpty(value)) { throw new ValidationError("An empty NameID value found", ValidationError.EMPTY_NAMEID); } @@ -699,8 +704,10 @@ public List getAudiences() throws XPathExpressionException { for (int i = 0; i < entries.getLength(); i++) { if (entries.item(i) != null) { String value = entries.item(i).getTextContent(); - if (value != null && !value.trim().isEmpty()) { - audiences.add(value.trim()); + if(value != null) + value = value.trim(); + if(!StringUtils.isEmpty(value)) { + audiences.add(value); } } } From 1a7b0da0567a48e7ea238de04bdf020d204de766 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Mon, 19 Apr 2021 12:33:46 +0200 Subject: [PATCH 074/133] Trim values obtained with getTextContent() on any XML node This change extends the previous one, made for SamlResponse name id, so that surrounding whitespace is removed for any value obtained from a XML element where this is indeed the expected behaviour (like in issuers, audiences, status messages, name ids). --- .../onelogin/saml2/authn/SamlResponse.java | 21 +++++++++++++++---- .../onelogin/saml2/logout/LogoutRequest.java | 20 +++++++++++++----- .../onelogin/saml2/logout/LogoutResponse.java | 15 +++++++------ .../java/com/onelogin/saml2/util/Util.java | 6 +++++- 4 files changed, 46 insertions(+), 16 deletions(-) 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 674a1680..d223f0c7 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -601,7 +601,11 @@ public HashMap> getAttributes() throws XPathExpressionExcep } for (int j = 0; j < childrens.getLength(); j++) { if ("AttributeValue".equals(childrens.item(j).getLocalName())) { - attrValues.add(childrens.item(j).getTextContent()); + String attrValue = childrens.item(j).getTextContent(); + if(attrValue != null) { + attrValue = attrValue.toString(); + } + attrValues.add(attrValue); } } @@ -704,8 +708,9 @@ public List getAudiences() throws XPathExpressionException { for (int i = 0; i < entries.getLength(); i++) { if (entries.item(i) != null) { String value = entries.item(i).getTextContent(); - if(value != null) + if(value != null) { value = value.trim(); + } if(!StringUtils.isEmpty(value)) { audiences.add(value); } @@ -729,7 +734,11 @@ public String getResponseIssuer() throws XPathExpressionException, ValidationErr NodeList responseIssuer = Util.query(samlResponseDocument, "/samlp:Response/saml:Issuer"); if (responseIssuer.getLength() > 0) { if (responseIssuer.getLength() == 1) { - return responseIssuer.item(0).getTextContent(); + String value = responseIssuer.item(0).getTextContent(); + if(value != null) { + value = value.trim(); + } + return value; } else { throw new ValidationError("Issuer of the Response is multiple.", ValidationError.ISSUER_MULTIPLE_IN_RESPONSE); } @@ -752,7 +761,11 @@ public String getResponseIssuer() throws XPathExpressionException, ValidationErr public String getAssertionIssuer() throws XPathExpressionException, ValidationError { NodeList assertionIssuer = this.queryAssertion("/saml:Issuer"); if (assertionIssuer.getLength() == 1) { - return assertionIssuer.item(0).getTextContent(); + String value = assertionIssuer.item(0).getTextContent(); + if(value != null) { + value = value.trim(); + } + return value; } else { throw new ValidationError("Issuer of the Assertion not found or multiple.", ValidationError.ISSUER_NOT_FOUND_IN_ASSERTION); } 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 e72e8914..98bf334f 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -586,7 +586,11 @@ public static Map getNameIdData(Document samlLogoutRequestDocume Map nameIdData = new HashMap(); if (nameIdElem != null) { - nameIdData.put("Value", nameIdElem.getTextContent()); + String value = nameIdElem.getTextContent(); + if(value != null) { + value = value.trim(); + } + nameIdData.put("Value", value); if (nameIdElem.hasAttribute("Format")) { nameIdData.put("Format", nameIdElem.getAttribute("Format")); @@ -702,9 +706,11 @@ public static String getIssuer(Document samlLogoutRequestDocument) throws XPathE NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:Issuer"); if (nodes.getLength() == 1) { - issuer = nodes.item(0).getTextContent(); - } - + issuer = nodes.item(0).getTextContent(); + } + if(issuer != null) { + issuer = issuer.trim(); + } return issuer; } @@ -740,7 +746,11 @@ public static List getSessionIndexes(Document samlLogoutRequestDocument) NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/samlp:SessionIndex"); for (int i = 0; i < nodes.getLength(); i++) { - sessionIndexes.add(nodes.item(i).getTextContent()); + String sessionIndex = nodes.item(i).getTextContent(); + if(sessionIndex != null) { + sessionIndex = sessionIndex.trim(); + sessionIndexes.add(sessionIndex); + } } return sessionIndexes; 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 9ebfc33a..085f631c 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -299,12 +299,15 @@ public Boolean isValid() { * @throws XPathExpressionException */ public String getIssuer() throws XPathExpressionException { - String issuer = null; - NodeList issuers = this.query("/samlp:LogoutResponse/saml:Issuer"); - if (issuers.getLength() == 1) { - issuer = issuers.item(0).getTextContent(); - } - return issuer; + String issuer = null; + NodeList issuers = this.query("/samlp:LogoutResponse/saml:Issuer"); + if (issuers.getLength() == 1) { + issuer = issuers.item(0).getTextContent(); + } + if (issuer != null) { + issuer = issuer.toString(); + } + return issuer; } /** 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 e2954f6a..48548c99 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -1647,7 +1647,11 @@ public static SamlResponseStatus getStatus(String statusXpath, Document dom) thr NodeList messageEntry = Util.query(dom, statusXpath + "/samlp:StatusMessage", (Element) statusEntry.item(0)); if (messageEntry.getLength() == 1) { - status.setStatusMessage(messageEntry.item(0).getTextContent()); + String statusMessage = messageEntry.item(0).getTextContent(); + if(statusMessage != null) { + statusMessage = statusMessage.trim(); + } + status.setStatusMessage(statusMessage); } return status; From ec625eb95d874a2ca1aa4b76f7cb3383d53ccf43 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 5 May 2021 18:10:32 +0200 Subject: [PATCH 075/133] Make Name ID and attribute value trimming an opt-in feature Name IDs (including issuers) are by default left untouched, as well as attribute values, like it was before. However two new settings have been introduced (whose default value is false) which allow to enable trimming for such values, which is probably the desired behaviour in practice, although SAML specification says that no whitespace processing should be performed on strings. Another place where trimming may be desirable is in SessionIndex extraction from LogoutRequests: this is not performed at any point of the LogoutRequest processing, but an overloading has been provided so that the API consumer may still request trimming. AuthnResponseTest.testGetIssuersTrimming() is disabled by now because it fails due to a bug in SamlResponse.getIssuers() which is addressed by another PR. --- README.md | 9 + .../onelogin/saml2/authn/SamlResponse.java | 10 +- .../onelogin/saml2/logout/LogoutRequest.java | 404 ++++++++++++------ .../onelogin/saml2/logout/LogoutResponse.java | 4 +- .../saml2/settings/Saml2Settings.java | 58 +++ .../saml2/settings/SettingsBuilder.java | 20 + .../java/com/onelogin/saml2/util/Util.java | 6 +- .../saml2/test/authn/AuthnResponseTest.java | 112 ++++- .../saml2/test/logout/LogoutRequestTest.java | 76 ++++ .../saml2/test/logout/LogoutResponseTest.java | 26 ++ .../test/settings/SettingBuilderTest.java | 29 ++ .../resources/config/config.all.properties | 9 + .../config/config.different.properties | 9 + .../logout_request_with_whitespace.xml | 19 + .../logout_response_with_whitespace.xml | 15 + ...logout_response_with_whitespace.xml.base64 | 1 + .../response3_with_whitespace.xml.base64 | 1 + 17 files changed, 659 insertions(+), 149 deletions(-) create mode 100644 core/src/test/resources/data/logout_requests/logout_request_with_whitespace.xml create mode 100644 core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml create mode 100644 core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml.base64 create mode 100644 core/src/test/resources/data/responses/response3_with_whitespace.xml.base64 diff --git a/README.md b/README.md index 3b33e053..d1bf4d3b 100644 --- a/README.md +++ b/README.md @@ -362,6 +362,15 @@ onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha2 # Reject Signatures with deprecated algorithms (sha1) onelogin.saml2.security.reject_deprecated_alg = true +# Enable trimming of parsed Name IDs and attribute values +# SAML specification states that no trimming for string elements should be performed, so no trimming will be +# performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add +# undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# attribute values. +onelogin.saml2.parsing.trim_name_ids = false +onelogin.saml2.parsing.trim_attribute_values = false + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example 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 d223f0c7..861db7bb 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -471,7 +471,7 @@ public Map getNameIdData() throws Exception { if (nameIdElem != null) { String value = nameIdElem.getTextContent(); - if(value != null) { + if(value != null && settings.isTrimNameIds()) { value = value.trim(); } if (settings.isStrict() && StringUtils.isEmpty(value)) { @@ -602,8 +602,8 @@ public HashMap> getAttributes() throws XPathExpressionExcep for (int j = 0; j < childrens.getLength(); j++) { if ("AttributeValue".equals(childrens.item(j).getLocalName())) { String attrValue = childrens.item(j).getTextContent(); - if(attrValue != null) { - attrValue = attrValue.toString(); + if(attrValue != null && settings.isTrimAttributeValues()) { + attrValue = attrValue.trim(); } attrValues.add(attrValue); } @@ -735,7 +735,7 @@ public String getResponseIssuer() throws XPathExpressionException, ValidationErr if (responseIssuer.getLength() > 0) { if (responseIssuer.getLength() == 1) { String value = responseIssuer.item(0).getTextContent(); - if(value != null) { + if(value != null && settings.isTrimNameIds()) { value = value.trim(); } return value; @@ -762,7 +762,7 @@ public String getAssertionIssuer() throws XPathExpressionException, ValidationEr NodeList assertionIssuer = this.queryAssertion("/saml:Issuer"); if (assertionIssuer.getLength() == 1) { String value = assertionIssuer.item(0).getTextContent(); - if(value != null) { + if(value != null && settings.isTrimNameIds()) { value = value.trim(); } return value; 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 98bf334f..366fd464 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -404,10 +404,10 @@ public Boolean isValid() { } // Try get the nameID - String nameID = getNameId(logoutRequestDocument, settings.getSPkey()); + String nameID = getNameId(logoutRequestDocument, settings.getSPkey(), settings.isTrimNameIds()); // Check the issuer - String issuer = getIssuer(logoutRequestDocument); + String issuer = getIssuer(logoutRequestDocument, settings.isTrimNameIds()); if (issuer != null && (issuer.isEmpty() || !issuer.equals(settings.getIdpEntityId()))) { throw new ValidationError( String.format("Invalid issuer in the Logout Request. Was '%s', but expected '%s'", issuer, settings.getIdpEntityId()), @@ -544,18 +544,36 @@ public static Calendar getIssueInstant(String samlLogoutRequestString) { } /** - * Gets the NameID Data from the the Logout Request Document. - * - * @param samlLogoutRequestDocument - * A DOMDocument object loaded from the SAML Logout Request. - * @param key - * The SP key to decrypt the NameID if encrypted - * - * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) - * + * Gets the NameID Data from the the Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * @param key + * The SP key to decrypt the NameID if encrypted + * + * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * * @throws Exception - */ + */ public static Map getNameIdData(Document samlLogoutRequestDocument, PrivateKey key) throws Exception { + return getNameIdData(samlLogoutRequestDocument, key, false); + } + + /** + * Gets the NameID Data from the the Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * @param key + * The SP key to decrypt the NameID if encrypted + * @param trimValue + * whether the extracted Name ID value should be trimmed + * + * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * + * @throws Exception + */ + public static Map getNameIdData(Document samlLogoutRequestDocument, PrivateKey key, boolean trimValue) throws Exception { NodeList encryptedIDNodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:EncryptedID"); NodeList nameIdNodes; Element nameIdElem; @@ -587,7 +605,7 @@ public static Map getNameIdData(Document samlLogoutRequestDocume if (nameIdElem != null) { String value = nameIdElem.getTextContent(); - if(value != null) { + if(value != null && trimValue) { value = value.trim(); } nameIdData.put("Value", value); @@ -606,169 +624,287 @@ public static Map getNameIdData(Document samlLogoutRequestDocume } /** - * Gets the NameID Data from the the Logout Request String. - * - * @param samlLogoutRequestString - * A DOMDocument object loaded from the SAML Logout Request. - * @param key - * The SP key to decrypt the NameID if encrypted - * - * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) - * + * Gets the NameID Data from the the Logout Request String. + * + * @param samlLogoutRequestString + * A DOMDocument object loaded from the SAML Logout Request. + * @param key + * The SP key to decrypt the NameID if encrypted + * + * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * * @throws Exception - */ + */ public static Map getNameIdData(String samlLogoutRequestString, PrivateKey key) throws Exception { + return getNameIdData(samlLogoutRequestString, key, false); + } + + /** + * Gets the NameID Data from the the Logout Request String. + * + * @param samlLogoutRequestString + * A DOMDocument object loaded from the SAML Logout Request. + * @param key + * The SP key to decrypt the NameID if encrypted + * @param trimValue + * whether the extracted Name ID value should be trimmed + * + * @return the Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * + * @throws Exception + */ + public static Map getNameIdData(String samlLogoutRequestString, PrivateKey key, boolean trimValue) throws Exception { Document doc = Util.loadXML(samlLogoutRequestString); - return getNameIdData(doc, key); + return getNameIdData(doc, key, trimValue); } /** - * Gets the NameID value provided from the SAML Logout Request Document. - * + * Gets the NameID value provided from the SAML Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * + * @param key + * The SP key to decrypt the NameID if encrypted + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(Document samlLogoutRequestDocument, PrivateKey key) throws Exception { + return getNameId(samlLogoutRequestDocument, key, false); + } + + /** + * Gets the NameID value provided from the SAML Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * + * @param key + * The SP key to decrypt the NameID if encrypted + * + * @param trimValue + * whether the extracted Name ID value should be trimmed + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(Document samlLogoutRequestDocument, PrivateKey key, boolean trimValue) + throws Exception { + Map nameIdData = getNameIdData(samlLogoutRequestDocument, key, trimValue); + LOGGER.debug("LogoutRequest has NameID --> " + nameIdData.get("Value")); + return nameIdData.get("Value"); + } + + /** + * Gets the NameID value provided from the SAML Logout Request Document. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(Document samlLogoutRequestDocument) throws Exception { + return getNameId(samlLogoutRequestDocument, null); + } + + /** + * Gets the NameID value provided from the SAML Logout Request String. + * + * @param samlLogoutRequestString + * A Logout Request string. + * @param key + * The SP key to decrypt the NameID if encrypted + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(String samlLogoutRequestString, PrivateKey key) throws Exception { + return getNameId(samlLogoutRequestString, key, false); + } + + /** + * Gets the NameID value provided from the SAML Logout Request String. + * + * @param samlLogoutRequestString + * A Logout Request string. + * @param key + * The SP key to decrypt the NameID if encrypted + * @param trimValue + * whether the extracted Name ID value should be trimmed + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(String samlLogoutRequestString, PrivateKey key, boolean trimValue) + throws Exception { + Map nameId = getNameIdData(samlLogoutRequestString, key, trimValue); + return nameId.get("Value"); + } + + /** + * Gets the NameID value provided from the SAML Logout Request String. + * + * @param samlLogoutRequestString + * A Logout Request string. + * + * @return the Name ID value + * + * @throws Exception + */ + public static String getNameId(String samlLogoutRequestString) throws Exception { + return getNameId(samlLogoutRequestString, null); + } + + /** + * Gets the Issuer from Logout Request Document. + * * @param samlLogoutRequestDocument - * A DOMDocument object loaded from the SAML Logout Request. - * @param key - * The SP key to decrypt the NameID if encrypted + * A DOMDocument object loaded from the SAML Logout Request. * - * @return the Name ID value + * @return the issuer of the logout request * - * @throws Exception + * @throws XPathExpressionException */ - public static String getNameId(Document samlLogoutRequestDocument, PrivateKey key) throws Exception - { - Map nameIdData = getNameIdData(samlLogoutRequestDocument, key); - LOGGER.debug("LogoutRequest has NameID --> " + nameIdData.get("Value")); - return nameIdData.get("Value"); + public static String getIssuer(Document samlLogoutRequestDocument) throws XPathExpressionException { + return getIssuer(samlLogoutRequestDocument, false); } - /** - * Gets the NameID value provided from the SAML Logout Request Document. - * + /** + * Gets the Issuer from Logout Request Document. + * * @param samlLogoutRequestDocument - * A DOMDocument object loaded from the SAML Logout Request. + * A DOMDocument object loaded from the SAML Logout Request. + * @param trim + * whether the extracted issuer value should be trimmed * - * @return the Name ID value + * @return the issuer of the logout request * - * @throws Exception + * @throws XPathExpressionException */ - public static String getNameId(Document samlLogoutRequestDocument) throws Exception - { - return getNameId(samlLogoutRequestDocument, null); + public static String getIssuer(Document samlLogoutRequestDocument, boolean trim) throws XPathExpressionException { + String issuer = null; + + NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:Issuer"); + + if (nodes.getLength() == 1) { + issuer = nodes.item(0).getTextContent(); + } + if (issuer != null && trim) { + issuer = issuer.trim(); + } + return issuer; } - - /** - * Gets the NameID value provided from the SAML Logout Request String. - * + + /** + * Gets the Issuer from Logout Request String. + * * @param samlLogoutRequestString - * A Logout Request string. - * @param key - * The SP key to decrypt the NameID if encrypted - * - * @return the Name ID value + * A Logout Request string. * - * @throws Exception + * @return the issuer of the logout request + * + * @throws XPathExpressionException */ - public static String getNameId(String samlLogoutRequestString, PrivateKey key) throws Exception - { - Map nameId = getNameIdData(samlLogoutRequestString, key); - return nameId.get("Value"); + public static String getIssuer(String samlLogoutRequestString) throws XPathExpressionException { + return getIssuer(samlLogoutRequestString, false); } - /** - * Gets the NameID value provided from the SAML Logout Request String. - * + /** + * Gets the Issuer from Logout Request String. + * * @param samlLogoutRequestString - * A Logout Request string. + * A Logout Request string. + * @param trim + * whether the extracted issuer value should be trimmed * - * @return the Name ID value - * - * @throws Exception + * @return the issuer of the logout request + * + * @throws XPathExpressionException */ - public static String getNameId(String samlLogoutRequestString) throws Exception - { - return getNameId(samlLogoutRequestString, null); - } - - /** - * Gets the Issuer from Logout Request Document. - * - * @param samlLogoutRequestDocument - * A DOMDocument object loaded from the SAML Logout Request. - * - * @return the issuer of the logout request - * - * @throws XPathExpressionException - */ - public static String getIssuer(Document samlLogoutRequestDocument) throws XPathExpressionException - { - String issuer = null; - - NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:Issuer"); - - if (nodes.getLength() == 1) { - issuer = nodes.item(0).getTextContent(); - } - if(issuer != null) { - issuer = issuer.trim(); - } - return issuer; + public static String getIssuer(String samlLogoutRequestString, boolean trim) throws XPathExpressionException { + Document doc = Util.loadXML(samlLogoutRequestString); + return getIssuer(doc, trim); } - /** - * Gets the Issuer from Logout Request String. - * - * @param samlLogoutRequestString - * A Logout Request string. - * - * @return the issuer of the logout request - * - * @throws XPathExpressionException - */ - public static String getIssuer(String samlLogoutRequestString) throws XPathExpressionException - { - Document doc = Util.loadXML(samlLogoutRequestString); - return getIssuer(doc); - } /** * Gets the SessionIndexes from the LogoutRequest. - * - * @param samlLogoutRequestDocument - * A DOMDocument object loaded from the SAML Logout Request. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. * @return the SessionIndexes * - * @throws XPathExpressionException + * @throws XPathExpressionException */ - public static List getSessionIndexes(Document samlLogoutRequestDocument) throws XPathExpressionException - { - List sessionIndexes = new ArrayList(); - - NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/samlp:SessionIndex"); + public static List getSessionIndexes(Document samlLogoutRequestDocument) throws XPathExpressionException { + return getSessionIndexes(samlLogoutRequestDocument, false); + } - for (int i = 0; i < nodes.getLength(); i++) { - String sessionIndex = nodes.item(i).getTextContent(); - if(sessionIndex != null) { - sessionIndex = sessionIndex.trim(); - sessionIndexes.add(sessionIndex); - } - } + /** + * Gets the SessionIndexes from the LogoutRequest. + * + * @param samlLogoutRequestDocument + * A DOMDocument object loaded from the SAML Logout Request. + * @param trim + * whether the extracted session indexes should be trimmed + * @return the SessionIndexes + * + * @throws XPathExpressionException + */ + public static List getSessionIndexes(Document samlLogoutRequestDocument, boolean trim) + throws XPathExpressionException { + List sessionIndexes = new ArrayList(); + + NodeList nodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/samlp:SessionIndex"); + + for (int i = 0; i < nodes.getLength(); i++) { + String sessionIndex = nodes.item(i).getTextContent(); + if (sessionIndex != null) { + if (trim) { + sessionIndex = sessionIndex.trim(); + } + sessionIndexes.add(sessionIndex); + } + } + + return sessionIndexes; + } - return sessionIndexes; + /** + * Gets the SessionIndexes from the LogoutRequest. + * + * @param samlLogoutRequestString + * A Logout Request string. + * @return the SessionIndexes + * + * @throws XPathExpressionException + */ + public static List getSessionIndexes(String samlLogoutRequestString) throws XPathExpressionException { + return getSessionIndexes(samlLogoutRequestString, false); } /** * Gets the SessionIndexes from the LogoutRequest. - * - * @param samlLogoutRequestString - * A Logout Request string. + * + * @param samlLogoutRequestString + * A Logout Request string. + * @param trim + * whether the extracted session indexes should be trimmed * @return the SessionIndexes * - * @throws XPathExpressionException + * @throws XPathExpressionException */ - public static List getSessionIndexes(String samlLogoutRequestString) throws XPathExpressionException - { - Document doc = Util.loadXML(samlLogoutRequestString); - return getSessionIndexes(doc); + public static List getSessionIndexes(String samlLogoutRequestString, boolean trim) + throws XPathExpressionException { + Document doc = Util.loadXML(samlLogoutRequestString); + return getSessionIndexes(doc, trim); } /** 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 085f631c..daf9da4d 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -304,8 +304,8 @@ public String getIssuer() throws XPathExpressionException { if (issuers.getLength() == 1) { issuer = issuers.item(0).getTextContent(); } - if (issuer != null) { - issuer = issuer.toString(); + if (issuer != null && settings.isTrimNameIds()) { + issuer = issuer.trim(); } return issuer; } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index f5c8565b..b2d2078a 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -82,6 +82,10 @@ public class Saml2Settings { // Compress private boolean compressRequest = true; private boolean compressResponse = true; + + // Parsing + private boolean trimNameIds = false; + private boolean trimAttributeValues = false; // Misc private List contacts = new LinkedList<>(); @@ -844,6 +848,60 @@ public boolean isCompressResponseEnabled() { return compressResponse; } + /** + * Sets whether Name IDs in parsed SAML messages should be trimmed. + *

+ * Default is false, that is Name IDs are kept intact, as the SAML + * specification prescribes. + * + * @param trimNameIds + * set to true to trim parsed Name IDs, set to + * false to keep them intact + */ + public void setTrimNameIds(boolean trimNameIds) { + this.trimNameIds = trimNameIds; + } + + /** + * Determines whether Name IDs should trimmed when extracting them from parsed + * SAML messages. + *

+ * Default is false, that is Name IDs are kept intact, as the SAML + * specification prescribes. + * + * @return true if Name IDs should be trimmed, false + * otherwise + */ + public boolean isTrimNameIds() { + return trimNameIds; + } + + /** + * Sets whether attribute values in parsed SAML messages should be trimmed. + *

+ * Default is false. + * + * @param trimAttributeValues + * set to true to trim parsed attribute values, set to + * false to keep them intact + */ + public void setTrimAttributeValues(boolean trimAttributeValues) { + this.trimAttributeValues = trimAttributeValues; + } + + /** + * Determines whether attribute values should be trimmed when extracting them + * from parsed SAML messages. + *

+ * Default is false. + * + * @return true if attribute values should be trimmed, + * false otherwise + */ + public boolean isTrimAttributeValues() { + return trimAttributeValues; + } + /** * Set contacts info that will be listed on the Service Provider metadata * diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index ac1ef92f..6c7a52ff 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -107,6 +107,10 @@ public class SettingsBuilder { public final static String COMPRESS_REQUEST = "onelogin.saml2.compress.request"; public final static String COMPRESS_RESPONSE = "onelogin.saml2.compress.response"; + // Parsing + public final static String PARSING_TRIM_NAME_IDS = "onelogin.saml2.parsing.trim_name_ids"; + public final static String PARSING_TRIM_ATTRIBUTE_VALUES = "onelogin.saml2.parsing.trim_attribute_values"; + // Misc public final static String CONTACT_TECHNICAL_GIVEN_NAME = "onelogin.saml2.contacts.technical.given_name"; public final static String CONTACT_TECHNICAL_EMAIL_ADDRESS = "onelogin.saml2.contacts.technical.email_address"; @@ -253,6 +257,7 @@ public Saml2Settings build(Saml2Settings saml2Setting) { this.loadIdpSetting(); this.loadSecuritySetting(); this.loadCompressSetting(); + this.loadParsingSetting(); List contacts = this.loadContacts(); if (!contacts.isEmpty()) { @@ -424,6 +429,21 @@ private void loadCompressSetting() { } } + /** + * Loads the parsing settings from the properties file + */ + private void loadParsingSetting() { + Boolean trimNameIds = loadBooleanProperty(PARSING_TRIM_NAME_IDS); + if (trimNameIds != null) { + saml2Setting.setTrimNameIds(trimNameIds); + } + + Boolean trimAttributeValues = loadBooleanProperty(PARSING_TRIM_ATTRIBUTE_VALUES); + if (trimAttributeValues != null) { + saml2Setting.setTrimAttributeValues(trimAttributeValues); + } + } + /** * Loads the organization settings from the properties file */ 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 48548c99..e2954f6a 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -1647,11 +1647,7 @@ public static SamlResponseStatus getStatus(String statusXpath, Document dom) thr NodeList messageEntry = Util.query(dom, statusXpath + "/samlp:StatusMessage", (Element) statusEntry.item(0)); if (messageEntry.getLength() == 1) { - String statusMessage = messageEntry.item(0).getTextContent(); - if(statusMessage != null) { - statusMessage = statusMessage.trim(); - } - status.setStatusMessage(statusMessage); + status.setStatusMessage(messageEntry.item(0).getTextContent()); } return status; 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 b7ee8a76..e4852c13 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 @@ -29,7 +29,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -445,6 +444,29 @@ public void testGetNameIdWrongEncryptedData() throws Exception { samlResponse.getNameId(); } + /** + * Tests the getNameId method of SamlResponse + *

+ * Case: with or without trimming + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.SamlResponse#getNameId + */ + @Test + public void testGetNameIdTrimming() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + settings.setStrict(false); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + String nameId = samlResponse.getNameId(); + assertEquals("\n \tsomeone@example.com\n ", nameId); + settings.setTrimNameIds(true); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + nameId = samlResponse.getNameId(); + assertEquals("someone@example.com", nameId); + } + /** * Tests the getNameIdData method of SamlResponse * @@ -584,7 +606,30 @@ public void testGetNameIdDataEmptyNameIDValue() throws Exception { expectedEx.expectMessage("An empty NameID value found"); samlResponse2.getNameIdData(); } - + + /** + * Tests the getNameIdData method of SamlResponse + *

+ * Case: with or without trimming + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.SamlResponse#getNameIdData + */ + @Test + public void testGetNameIdDataTrimming() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + settings.setStrict(false); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + Map nameIdData = samlResponse.getNameIdData(); + assertEquals("\n \tsomeone@example.com\n ", nameIdData.get("Value")); + settings.setTrimNameIds(true); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + nameIdData = samlResponse.getNameIdData(); + assertEquals("someone@example.com", nameIdData.get("Value")); + } + /** * Tests the checkOneCondition method of SamlResponse * @@ -971,8 +1016,34 @@ public void testGetAssertionIssuerNoInAssertion() throws IOException, Error, XPa } /** - * Tests the getIssuers methods of SamlResponse + * Tests the getIssuers method of SamlResponse *

+ * Case: with or without trimming + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetIssuersTrimming() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + // disabled by now +// Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); +// String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); +// SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); +// assertEquals(Arrays.asList("\n \thttp://example.com/services/trust\n ", "\n \thttp://example.com/services/trust\n "), samlResponse.getIssuers()); +// settings.setTrimNameIds(true); +// samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); +// assertEquals(Arrays.asList("http://example.com/services/trust"), samlResponse.getIssuers()); + } + + /** + * Tests the getIssuers method of SamlResponse * Case: Issuer of the assertion not found * * @throws Error @@ -1101,6 +1172,41 @@ public void testGetAttributes() throws IOException, Error, XPathExpressionExcept assertTrue(samlResponse.getAttributes().isEmpty()); } + /** + * Tests the getAttributes method of SamlResponse + *

+ * Case: with or without trimming + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getAttributes + */ + @Test + public void testGetAttributesTrimming() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + HashMap> expectedAttributes = new HashMap>(); + List attrValuesTrimmed = new ArrayList(); + attrValuesTrimmed.add("someone@example.com"); + List attrValuesNonTrimmed = new ArrayList(); + attrValuesNonTrimmed.add("\n \tsomeone@example.com\n "); + + expectedAttributes.put("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", attrValuesNonTrimmed); + assertEquals(expectedAttributes, samlResponse.getAttributes()); + + settings.setTrimAttributeValues(true); + expectedAttributes.put("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", attrValuesTrimmed); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(expectedAttributes, samlResponse.getAttributes()); + } + /** * Tests the getAttributes method of SamlResponse * Case: Duplicated names diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index f335a12b..8b6b0246 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -12,7 +12,9 @@ import java.util.ArrayList; import java.util.Calendar; +import java.util.Arrays; import java.util.List; +import java.util.Map; import java.io.IOException; import java.net.URISyntaxException; @@ -415,6 +417,22 @@ public void testGetNameIdDataNoNameId() throws Exception { LogoutRequest.getNameIdData(logoutRequestStr, null).toString(); } + /** + * Tests the getNameIdData method of LogoutRequest. + *

+ * Case: with or without trimming + * + * @throws Exception + */ + @Test + public void testGetNameIdDataTrimming() throws Exception { + String logoutRequestStr = Util.getFileAsString("data/logout_requests/logout_request_with_whitespace.xml"); + Map nameIdData = LogoutRequest.getNameIdData(logoutRequestStr, null, true); + assertEquals("ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", nameIdData.get("Value")); + nameIdData = LogoutRequest.getNameIdData(logoutRequestStr, null, false); + assertEquals("\n\t\tONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c\n\t", nameIdData.get("Value")); + } + /** * Tests the getNameId method of LogoutRequest * Case: Able to get the NameID @@ -507,6 +525,22 @@ public void testGetNameIdWrongKey() throws Exception { LogoutRequest.getNameIdData(logoutRequestStr, key).toString(); } + /** + * Tests the getNameId method of LogoutRequest. + *

+ * Case: with or without trimming + * + * @throws Exception + */ + @Test + public void testGetNameIdTrimming() throws Exception { + String logoutRequestStr = Util.getFileAsString("data/logout_requests/logout_request_with_whitespace.xml"); + String nameId = LogoutRequest.getNameId(logoutRequestStr, null, true); + assertEquals("ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", nameId); + nameId = LogoutRequest.getNameId(logoutRequestStr, null, false); + assertEquals("\n\t\tONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c\n\t", nameId); + } + /** * Tests the getIssuer method of LogoutRequest * @@ -528,6 +562,26 @@ public void testGetIssuer() throws URISyntaxException, IOException, XPathExpress assertNull(issuer); } + /** + * Tests the getIssuer method of LogoutRequest + *

+ * Case: with or without trimming + * + * @throws IOException + * @throws URISyntaxException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.logout.LogoutRequest#getIssuer + */ + @Test + public void testGetIssuerTrimming() throws URISyntaxException, IOException, XPathExpressionException { + String logoutRequestStr = Util.getFileAsString("data/logout_requests/logout_request_with_whitespace.xml"); + String issuer = LogoutRequest.getIssuer(logoutRequestStr, true); + assertEquals("http://idp.example.com/", issuer); + issuer = LogoutRequest.getIssuer(logoutRequestStr, false); + assertEquals("\n \thttp://idp.example.com/\n ", issuer); + } + /** * Tests the getSessionIndexes method of LogoutRequest * @@ -556,6 +610,28 @@ public void testGetSessionIndexes() throws URISyntaxException, IOException, XPat assertEquals(expectedIndexes, indexes); } + /** + * Tests the getSessionIndexes method of LogoutRequest + *

+ * Case: with or without trimming + * + * @throws IOException + * @throws URISyntaxException + * @throws XPathExpressionException + * @throws XMLEntityException + * @throws Error + * + * @see com.onelogin.saml2.logout.LogoutRequest#getSessionIndexes + */ + @Test + public void testGetSessionIndexesTrimming() throws IOException, XPathExpressionException { + String logoutRequestStr = Util.getFileAsString("data/logout_requests/logout_request_with_whitespace.xml"); + List indexes = LogoutRequest.getSessionIndexes(logoutRequestStr, true); + assertEquals(Arrays.asList("_ac72a76526cb6ca19f8438e73879a0e6c8ae5131"), indexes); + indexes = LogoutRequest.getSessionIndexes(logoutRequestStr, false); + assertEquals(Arrays.asList("\n\t\t_ac72a76526cb6ca19f8438e73879a0e6c8ae5131\n\t"), indexes); + } + /** * Tests the isValid method of LogoutRequest * Case: Invalid Issuer diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index 7bb373b0..9ce92dc4 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -290,6 +290,32 @@ public void testGetIssueInstantBuiltMessage() throws IOException, Error, Validat long millis = issueInstant.getTimeInMillis(); assertTrue(millis >= start && millis <= end); } + + /** + * Tests the getIssuer method of LogoutResponse + *

+ * Case: with or without trimming + * + * @throws IOException + * @throws URISyntaxException + * @throws XMLEntityException + * @throws XPathExpressionException + * @throws Error + * + * @see com.onelogin.saml2.logout.LogoutResponse#getIssuer + */ + @Test + public void testGetIssuerTrimming() throws Error, IOException, XPathExpressionException { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/logout_responses/logout_response_with_whitespace.xml.base64"); + HttpRequest httpRequest = newHttpRequest("/", samlResponseEncoded); + LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); + assertEquals("\n \thttp://idp.example.com/\n ", logoutResponse.getIssuer()); + + settings.setTrimNameIds(true); + logoutResponse = new LogoutResponse(settings, httpRequest); + assertEquals("http://idp.example.com/", logoutResponse.getIssuer()); + } /** * Tests the isValid method of LogoutResponse diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java index 956a343d..f9e31dde 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java @@ -161,6 +161,9 @@ public void testLoadFromFileEmpty() throws IOException, CertificateException, UR assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); + assertFalse(setting.isTrimNameIds()); + assertFalse(setting.isTrimAttributeValues()); + assertNull(setting.getOrganization()); assertTrue(setting.getContacts().isEmpty()); } @@ -216,6 +219,9 @@ public void testLoadFromFileMinProp() throws IOException, CertificateException, assertEquals(Constants.RSA_SHA1, setting.getSignatureAlgorithm()); assertEquals(Constants.SHA1, setting.getDigestAlgorithm()); assertFalse(setting.getSignMetadata()); + + assertFalse(setting.isTrimNameIds()); + assertFalse(setting.isTrimAttributeValues()); assertNull(setting.getOrganization()); assertTrue(setting.getContacts().isEmpty()); @@ -278,6 +284,9 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); + assertFalse(setting.isTrimNameIds()); + assertFalse(setting.isTrimAttributeValues()); + Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); assertTrue(org.equalsTo(setting.getOrganization())); @@ -569,6 +578,9 @@ public void testLoadFromFileDifferentProp() throws IOException, CertificateExcep assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); assertTrue(setting.getSignMetadata()); + assertTrue(setting.isTrimNameIds()); + assertTrue(setting.isTrimAttributeValues()); + Organization org = new Organization("SP Java", "", ""); assertTrue(org.equalsTo(setting.getOrganization())); @@ -662,6 +674,9 @@ public void testFromProperties() throws IOException, Error, CertificateException assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); assertFalse(setting2.getSignMetadata()); + assertFalse(setting.isTrimNameIds()); + assertFalse(setting.isTrimAttributeValues()); + assertNull(setting2.getOrganization()); assertTrue(setting2.getContacts().isEmpty()); @@ -726,6 +741,10 @@ public void testLoadFromValues() throws Exception { // Compress samlData.put(COMPRESS_REQUEST, "false"); samlData.put(COMPRESS_RESPONSE, "false"); + + // Parsing + samlData.put(PARSING_TRIM_NAME_IDS, "true"); + samlData.put(PARSING_TRIM_ATTRIBUTE_VALUES, "true"); // Organization samlData.put(ORGANIZATION_NAME, "SP Java"); @@ -792,6 +811,9 @@ public void testLoadFromValues() throws Exception { assertFalse(setting.isCompressRequestEnabled()); assertFalse(setting.isCompressResponseEnabled()); + + assertTrue(setting.isTrimNameIds()); + assertTrue(setting.isTrimAttributeValues()); Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); assertTrue(org.equalsTo(setting.getOrganization())); @@ -884,6 +906,10 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(COMPRESS_REQUEST, "false"); samlData.put(COMPRESS_RESPONSE, "false"); + // Parsing + samlData.put(PARSING_TRIM_NAME_IDS, "true"); + samlData.put(PARSING_TRIM_ATTRIBUTE_VALUES, "true"); + // Organization samlData.put(ORGANIZATION_NAME, "SP Java"); samlData.put(ORGANIZATION_DISPLAYNAME, "SP Java Example"); @@ -942,6 +968,9 @@ public void testLoadFromValuesWithObjects() throws Exception { assertFalse(setting.isCompressRequestEnabled()); assertFalse(setting.isCompressResponseEnabled()); + assertTrue(setting.isTrimNameIds()); + assertTrue(setting.isTrimAttributeValues()); + Organization org = new Organization("SP Java", "SP Java Example", "http://sp.example.com"); assertTrue(org.equalsTo(setting.getOrganization())); diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index 2e879d5b..b2741928 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -137,6 +137,15 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- # 'http://www.w3.org/2001/04/xmlenc#sha512' onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 +# Enable trimming of parsed Name IDs and attribute values +# SAML specification states that no trimming for string elements should be performed, so no trimming will be +# performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add +# undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# attribute values. +onelogin.saml2.parsing.trim_name_ids = false +onelogin.saml2.parsing.trim_attribute_values = false + # Organization onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example diff --git a/core/src/test/resources/config/config.different.properties b/core/src/test/resources/config/config.different.properties index 84c392c9..75f2cda2 100644 --- a/core/src/test/resources/config/config.different.properties +++ b/core/src/test/resources/config/config.different.properties @@ -134,6 +134,15 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- # 'http://www.w3.org/2001/04/xmlenc#sha512' onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 +# Enable trimming of parsed Name IDs and attribute values +# SAML specification states that no trimming for string elements should be performed, so no trimming will be +# performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add +# undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# attribute values. +onelogin.saml2.parsing.trim_name_ids = true +onelogin.saml2.parsing.trim_attribute_values = true + # Organization onelogin.saml2.organization.name = SP Java diff --git a/core/src/test/resources/data/logout_requests/logout_request_with_whitespace.xml b/core/src/test/resources/data/logout_requests/logout_request_with_whitespace.xml new file mode 100644 index 00000000..ee7db57f --- /dev/null +++ b/core/src/test/resources/data/logout_requests/logout_request_with_whitespace.xml @@ -0,0 +1,19 @@ + + + + http://idp.example.com/ + + + ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c + + + _ac72a76526cb6ca19f8438e73879a0e6c8ae5131 + + diff --git a/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml b/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml new file mode 100644 index 00000000..bba89c5f --- /dev/null +++ b/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml @@ -0,0 +1,15 @@ + + + http://idp.example.com/ + + + + + diff --git a/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml.base64 b/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml.base64 new file mode 100644 index 00000000..1dc59d22 --- /dev/null +++ b/core/src/test/resources/data/logout_responses/logout_response_with_whitespace.xml.base64 @@ -0,0 +1 @@ +PHNhbWxwOkxvZ291dFJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiCiAgICAgICAgICAgICAgICAgICAgICB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIgogICAgICAgICAgICAgICAgICAgICAgSUQ9Il9mOWVlNjFiZDlkYmY2MzYwNmZhYTlhZTNiMTA1NDhkNWIzNjU2ZmI4NTkiCiAgICAgICAgICAgICAgICAgICAgICBWZXJzaW9uPSIyLjAiCiAgICAgICAgICAgICAgICAgICAgICBJc3N1ZUluc3RhbnQ9IjIwMTMtMTItMTBUMDQ6Mzk6MzFaIgogICAgICAgICAgICAgICAgICAgICAgRGVzdGluYXRpb249Imh0dHA6Ly9zdHVmZi5jb20vZW5kcG9pbnRzL2VuZHBvaW50cy9zbHMucGhwIgogICAgICAgICAgICAgICAgICAgICAgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl8yMTU4NGNjZGZhY2EzNmExNDVhZTk5MDQ0MmRjZDk2YmZlNjAxNTFlIgogICAgICAgICAgICAgICAgICAgICAgPgogICAgPHNhbWw6SXNzdWVyPgogICAgCWh0dHA6Ly9pZHAuZXhhbXBsZS5jb20vCiAgICA8L3NhbWw6SXNzdWVyPgogICAgPHNhbWxwOlN0YXR1cz4KICAgICAgICA8c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIiAvPgogICAgPC9zYW1scDpTdGF0dXM+Cjwvc2FtbHA6TG9nb3V0UmVzcG9uc2U+Cg== \ No newline at end of file diff --git a/core/src/test/resources/data/responses/response3_with_whitespace.xml.base64 b/core/src/test/resources/data/responses/response3_with_whitespace.xml.base64 new file mode 100644 index 00000000..c9843e95 --- /dev/null +++ b/core/src/test/resources/data/responses/response3_with_whitespace.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iXzZiODVkMGRkLWJmYTgtNGRlZi04MmMyLTg2MjFlMDQ1MjQ3NyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDUtMDJUMTk6NDM6NTQuNjkyWiIgRGVzdGluYXRpb249Imh0dHBzOi8vZXhhbXBsZS5jb20vYWNjZXNzL3NhbWwiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBJblJlc3BvbnNlVG89Il9mYjg0MThkMC01NzFlLTAxMmUtZWVlMC0wMDUwNTY5MjAwZDAiPgogIDxJc3N1ZXIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPgogIAlodHRwOi8vZXhhbXBsZS5jb20vc2VydmljZXMvdHJ1c3QKICA8L0lzc3Vlcj4KICA8c2FtbHA6U3RhdHVzPgogICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPgogIDwvc2FtbHA6U3RhdHVzPgogIDxBc3NlcnRpb24geG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfZGJlNmEzNjUtOTU4Mi00NjBmLWI0YjEtMWY3OWJmNzBmNzZiIiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDUtMDJUMTk6NDM6NTQuNjQ1WiIgVmVyc2lvbj0iMi4wIj4KICAgIDxJc3N1ZXI+CiAgICAJaHR0cDovL2V4YW1wbGUuY29tL3NlcnZpY2VzL3RydXN0CiAgICA8L0lzc3Vlcj4KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogICAgICA8ZHM6U2lnbmVkSW5mbz4KICAgICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICAgICAgICA8ZHM6UmVmZXJlbmNlIFVSST0iI19kYmU2YTM2NS05NTgyLTQ2MGYtYjRiMS0xZjc5YmY3MGY3NmIiPgogICAgICAgICAgPGRzOlRyYW5zZm9ybXM+CiAgICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPgogICAgICAgICAgICA8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICAgICAgICA8L2RzOlRyYW5zZm9ybXM+CiAgICAgICAgICA8ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz4KICAgICAgICAgIDxkczpEaWdlc3RWYWx1ZT5EaWdlc3Q8L2RzOkRpZ2VzdFZhbHVlPgogICAgICAgIDwvZHM6UmVmZXJlbmNlPgogICAgICA8L2RzOlNpZ25lZEluZm8+CiAgICAgIDxkczpTaWduYXR1cmVWYWx1ZT5TaWduYXR1cmU8L2RzOlNpZ25hdHVyZVZhbHVlPgogICAgICA8S2V5SW5mbyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgICAgICAgPGRzOlg1MDlEYXRhPgogICAgICAgICAgPGRzOlg1MDlDZXJ0aWZpY2F0ZT5TdHVmZjwvZHM6WDUwOUNlcnRpZmljYXRlPgogICAgICAgIDwvZHM6WDUwOURhdGE+CiAgICAgIDwvS2V5SW5mbz4KICAgIDwvZHM6U2lnbmF0dXJlPgogICAgPFN1YmplY3Q+CiAgICAgIDxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPgogICAgICAJc29tZW9uZUBleGFtcGxlLmNvbQogICAgICA8L05hbWVJRD4KICAgICAgPFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfZmI4NDE4ZDAtNTcxZS0wMTJlLWVlZTAtMDA1MDU2OTIwMGQwIiBOb3RPbk9yQWZ0ZXI9IjIwMTEtMDUtMDJUMTk6NDg6NTQuNzA3WiIgUmVjaXBpZW50PSJodHRwczovL2V4YW1wbGUuY29tL2FjY2Vzcy9zYW1sIi8+CiAgICAgIDwvU3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvU3ViamVjdD4KICAgIDxDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMS0wNS0wMlQxOTo0Mzo1NC42NDVaIiBOb3RPbk9yQWZ0ZXI9IjIwMTEtMDUtMDJUMjA6NDM6NTQuNjQ1WiI+CiAgICAgIDxBdWRpZW5jZVJlc3RyaWN0aW9uPgogICAgICAgIDxBdWRpZW5jZT5jb25zdW1lci5leGFtcGxlLmNvbTwvQXVkaWVuY2U+CiAgICAgIDwvQXVkaWVuY2VSZXN0cmljdGlvbj4KICAgIDwvQ29uZGl0aW9ucz4KICAgIDxBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgIDxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZW1haWxhZGRyZXNzIj4KICAgICAgICA8QXR0cmlidXRlVmFsdWU+CiAgICAgICAgCXNvbWVvbmVAZXhhbXBsZS5jb20KICAgICAgICA8L0F0dHJpYnV0ZVZhbHVlPgogICAgICA8L0F0dHJpYnV0ZT4KICAgIDwvQXR0cmlidXRlU3RhdGVtZW50PgogICAgPEF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxMS0wNS0wMlQxOTo0Mzo1NC4yODVaIiBTZXNzaW9uSW5kZXg9Il9kYmU2YTM2NS05NTgyLTQ2MGYtYjRiMS0xZjc5YmY3MGY3NmIiPgogICAgICA8QXV0aG5Db250ZXh0PgogICAgICAgIDxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvQXV0aG5Db250ZXh0Q2xhc3NSZWY+CiAgICAgIDwvQXV0aG5Db250ZXh0PgogICAgPC9BdXRoblN0YXRlbWVudD4KICA8L0Fzc2VydGlvbj4KPC9zYW1scDpSZXNwb25zZT4K \ No newline at end of file From 15627843b77270e96225eb016bb56783bdba59fd Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 14 Jul 2021 14:39:25 +0200 Subject: [PATCH 076/133] Adjust AuthnResponseTest with regards to issuers trimming Now that fixes to the getIssuers() and the new getResponseIssuer() and getAssertionIssuer() are available on master, proper test cases can be provided with regards to trimming. --- .../saml2/test/authn/AuthnResponseTest.java | 125 +++++++++++++----- 1 file changed, 90 insertions(+), 35 deletions(-) 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 e4852c13..1b66822a 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 @@ -29,6 +29,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -890,6 +891,86 @@ public void testGetAudiences() throws IOException, Error, XPathExpressionExcepti assertEquals(expectedAudiences, samlResponse.getAudiences()); } + /** + * Tests the getResponseIssuer method of SamlResponse + *

+ * Case: with or without trimming + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetResponseIssuerTrimming() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + // disabled by now + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals("\n \thttp://example.com/services/trust\n ", samlResponse.getResponseIssuer()); + settings.setTrimNameIds(true); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals("http://example.com/services/trust", samlResponse.getResponseIssuer()); + } + + /** + * Tests the getAssertionIssuer method of SamlResponse + *

+ * Case: Issuer of the assertion not found + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetAssertionIssuerNoInAssertion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_assertion.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + + expectedEx.expect(ValidationError.class); + expectedEx.expectMessage("Issuer of the Assertion not found or multiple."); + samlResponse.getAssertionIssuer(); + } + + /** + * Tests the getAssertionIssuer method of SamlResponse + *

+ * Case: with or without trimming + * + * @throws Error + * @throws IOException + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#getIssuers + */ + @Test + public void testGetAssertionIssuerTrimming() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + // disabled by now + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals("\n \thttp://example.com/services/trust\n ", samlResponse.getAssertionIssuer()); + settings.setTrimNameIds(true); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals("http://example.com/services/trust", samlResponse.getAssertionIssuer()); + } + /** * Tests the getIssuers methods of SamlResponse * @@ -990,8 +1071,7 @@ public void testGetIssuersDifferentIssuers() throws IOException, Error, XPathExp } /** - * Tests the getAssertionIssuer method of SamlResponse - *

+ * Tests the getIssuers method of SamlResponse * Case: Issuer of the assertion not found * * @throws Error @@ -1005,14 +1085,15 @@ public void testGetIssuersDifferentIssuers() throws IOException, Error, XPathExp * @see com.onelogin.saml2.authn.SamlResponse#getIssuers */ @Test - public void testGetAssertionIssuerNoInAssertion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + public void testGetIssuersNoInAssertion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_assertion.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + samlResponse.getResponseIssuer(); // this should not fail expectedEx.expect(ValidationError.class); expectedEx.expectMessage("Issuer of the Assertion not found or multiple."); - samlResponse.getAssertionIssuer(); + samlResponse.getIssuers(); } /** @@ -1033,39 +1114,13 @@ public void testGetAssertionIssuerNoInAssertion() throws IOException, Error, XPa @Test public void testGetIssuersTrimming() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { // disabled by now -// Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); -// String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); -// SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); -// assertEquals(Arrays.asList("\n \thttp://example.com/services/trust\n ", "\n \thttp://example.com/services/trust\n "), samlResponse.getIssuers()); -// settings.setTrimNameIds(true); -// samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); -// assertEquals(Arrays.asList("http://example.com/services/trust"), samlResponse.getIssuers()); - } - - /** - * Tests the getIssuers method of SamlResponse - * Case: Issuer of the assertion not found - * - * @throws Error - * @throws IOException - * @throws ValidationError - * @throws SettingsException - * @throws SAXException - * @throws ParserConfigurationException - * @throws XPathExpressionException - * - * @see com.onelogin.saml2.authn.SamlResponse#getIssuers - */ - @Test - public void testGetIssuersNoInAssertion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build(); - String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_issuer_assertion.xml.base64"); + String samlResponseEncoded = Util.getFileAsString("data/responses/response3_with_whitespace.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); - - samlResponse.getResponseIssuer(); // this should not fail - expectedEx.expect(ValidationError.class); - expectedEx.expectMessage("Issuer of the Assertion not found or multiple."); - samlResponse.getIssuers(); + assertEquals(Arrays.asList("\n \thttp://example.com/services/trust\n ", "\n \thttp://example.com/services/trust\n "), samlResponse.getIssuers()); + settings.setTrimNameIds(true); + samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertEquals(Arrays.asList("http://example.com/services/trust"), samlResponse.getIssuers()); } /** From 44ec6f5ae0e8377fb9e51e420b2f5fabf87ce682 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 23 Jul 2021 15:19:27 +0200 Subject: [PATCH 077/133] Remove useless XML escaping of Base64 certificate data --- core/src/main/java/com/onelogin/saml2/settings/Metadata.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index e5ee5c17..9b158f14 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -318,7 +318,7 @@ private String toX509KeyDescriptorsXML(X509Certificate certCurrent, X509Certific keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+Util.toXml(certString)+""); + keyDescriptorXml.append(""+certString+""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); @@ -327,7 +327,7 @@ private String toX509KeyDescriptorsXML(X509Certificate certCurrent, X509Certific keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); - keyDescriptorXml.append(""+Util.toXml(certString)+""); + keyDescriptorXml.append(""+certString+""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); keyDescriptorXml.append(""); From e71392df262be273abcc034ed83165da6aebcdc1 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 1 Apr 2021 18:57:11 +0200 Subject: [PATCH 078/133] Allow for extension classes to post-process generated XML This change allows for any java-saml consumer to extend the standard classes used to generate SAML messages (AuthnRequest, LogoutRequest and LogoutResponse), as well as the metadata, and provide their own logic to post-process the default XML produced by java-saml. Any extension class will then be able to transform or enrich the generated XML as required, before the framework applies encoding, encryption or signing. --- .../onelogin/saml2/authn/AuthnRequest.java | 20 ++++++++++++++++- .../onelogin/saml2/logout/LogoutRequest.java | 22 ++++++++++++++++++- .../onelogin/saml2/logout/LogoutResponse.java | 20 ++++++++++++++++- .../com/onelogin/saml2/settings/Metadata.java | 20 ++++++++++++++++- .../saml2/test/authn/AuthnRequestTest.java | 19 ++++++++++++++++ .../saml2/test/logout/LogoutRequestTest.java | 19 ++++++++++++++++ .../saml2/test/logout/LogoutResponseTest.java | 20 +++++++++++++++++ .../saml2/test/settings/MetadataTest.java | 19 ++++++++++++++++ 8 files changed, 155 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index 946d07d7..2483ea5e 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -101,7 +101,7 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv this.nameIdValueReq = nameIdValueReq; StrSubstitutor substitutor = generateSubstitutor(settings); - authnRequestString = substitutor.replace(getAuthnRequestTemplate()); + authnRequestString = postProcessXml(substitutor.replace(getAuthnRequestTemplate())); LOGGER.debug("AuthNRequest --> " + authnRequestString); } @@ -121,6 +121,24 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv this(settings, forceAuthn, isPassive, setNameIdPolicy, null); } + /** + * Allows for an extension class to post-process the AuthnRequest XML generated + * for this request, in order to customize the result. + *

+ * This method is invoked at construction time, after all the other fields of + * this class have already been initialised. Its default implementation simply + * returns the input XML as-is, with no change. + * + * @param authRequestXml + * the XML produced for this AuthnRequest by the standard + * implementation provided by {@link AuthnRequest} + * @return the post-processed XML for this AuthnRequest, which will then be + * returned by any call to {@link #getAuthnRequestXml()} + */ + protected String postProcessXml(final String authRequestXml) { + return authRequestXml; + } + /** * @return the base64 encoded unsigned AuthnRequest (deflated or not) * 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 e72e8914..c1572e09 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -139,7 +139,7 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, this.sessionIndex = sessionIndex; StrSubstitutor substitutor = generateSubstitutor(settings); - logoutRequestString = substitutor.replace(getLogoutRequestTemplate()); + logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate())); } else { logoutRequestString = Util.base64decodedInflated(samlLogoutRequest); Document doc = Util.loadXML(logoutRequestString); @@ -224,6 +224,26 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request) { this(settings, request, null, null); } + /** + * Allows for an extension class to post-process the LogoutRequest XML generated + * for this request, in order to customize the result. + *

+ * This method is invoked at construction time when no existing LogoutRequest + * message is found in the HTTP request (and hence in the logout request sending + * scenario only), after all the other fields of this class have already been + * initialised. Its default implementation simply returns the input XML as-is, + * with no change. + * + * @param logoutRequestXml + * the XML produced for this LogoutRequest by the standard + * implementation provided by {@link LogoutRequest} + * @return the post-processed XML for this LogoutRequest, which will then be + * returned by any call to {@link #getLogoutRequestXml()} + */ + protected String postProcessXml(final String logoutRequestXml) { + return logoutRequestXml; + } + /** * @return the base64 encoded unsigned Logout Request (deflated or not) * 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 9ebfc33a..38814ff7 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -364,7 +364,7 @@ public void build(String inResponseTo, String statusCode) { this.inResponseTo = inResponseTo; StrSubstitutor substitutor = generateSubstitutor(settings, statusCode); - this.logoutResponseString = substitutor.replace(getLogoutResponseTemplate()); + this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate())); } /** @@ -385,6 +385,24 @@ public void build() { build(null); } + /** + * Allows for an extension class to post-process the LogoutResponse XML + * generated for this response, in order to customize the result. + *

+ * This method is invoked by {@link #build(String, String)} (and all of its + * overloadings) and hence only in the logout response sending scenario. Its + * default implementation simply returns the input XML as-is, with no change. + * + * @param logoutResponseXml + * the XML produced for this LogoutResponse by the standard + * implementation provided by {@link LogoutResponse} + * @return the post-processed XML for this LogoutResponse, which will then be + * returned by any call to {@link #getLogoutResponseXml()} + */ + protected String postProcessXml(final String logoutResponseXml) { + return logoutResponseXml; + } + /** * Substitutes LogoutResponse variables within a string by values. * diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 7d6e8d91..42183441 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -109,11 +109,29 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { this.cacheDuration = SECONDS_CACHED; StrSubstitutor substitutor = generateSubstitutor(settings); - String unsignedMetadataString = substitutor.replace(getMetadataTemplate()); + String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate())); LOGGER.debug("metadata --> " + unsignedMetadataString); metadataString = unsignedMetadataString; } + + /** + * Allows for an extension class to post-process the SAML metadata XML generated + * for this metadata instance, in order to customize the result. + *

+ * This method is invoked at construction time, after all the other fields of + * this class have already been initialised. Its default implementation simply + * returns the input XML as-is, with no change. + * + * @param metadataXml + * the XML produced for this metadata instance by the standard + * implementation provided by {@link Metadata} + * @return the post-processed XML for this metadata instance, which will then be + * returned by any call to {@link #getMetadataString()} + */ + protected String postProcessXml(final String metadataXml) { + return metadataXml; + } /** * Substitutes metadata variables within a string by values. diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 7b31a111..31fd4f55 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -382,4 +382,23 @@ public void testAuthNDestination() throws Exception { assertThat(authnRequestStr, containsString(" Date: Wed, 7 Apr 2021 10:33:23 +0200 Subject: [PATCH 079/133] Add protected getter for settings to ease extension The various SAML message and metadata object classes have now a protected getter that allows for subclasses to access the settings specified at construction time. This is useful to ease extension, for instance when implementing postProcessXml, so that extensions don't need to save their own copy of the settings. --- .../com/onelogin/saml2/authn/AuthnRequest.java | 9 +++++++++ .../com/onelogin/saml2/authn/SamlResponse.java | 17 +++++++++++++---- .../onelogin/saml2/logout/LogoutRequest.java | 9 +++++++++ .../onelogin/saml2/logout/LogoutResponse.java | 9 +++++++++ .../com/onelogin/saml2/settings/Metadata.java | 17 ++++++++++++++++- 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index 2483ea5e..bd68ddea 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -288,4 +288,13 @@ public String getId() public Calendar getIssueInstant() { return issueInstant == null? null: (Calendar) issueInstant.clone(); } + + /** + * Returns the SAML settings specified at construction time. + * + * @return the SAML settings + */ + protected Saml2Settings getSettings() { + return settings; + } } 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 b3022acb..364813d1 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -41,13 +41,13 @@ */ public class SamlResponse { /** - * Private property to construct a logger for this class. - */ + * Private property to construct a logger for this class. + */ private static final Logger LOGGER = LoggerFactory.getLogger(SamlResponse.class); /** - * Settings data. - */ + * Settings data. + */ private final Saml2Settings settings; /** @@ -1322,4 +1322,13 @@ public Calendar getResponseIssueInstant() throws ValidationError { } return result; } + + /** + * Returns the SAML settings specified at construction time. + * + * @return the SAML settings + */ + protected Saml2Settings getSettings() { + return settings; + } } 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 c1572e09..be30bf83 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -829,4 +829,13 @@ public String getId() public Calendar getIssueInstant() { return issueInstant == null? null: (Calendar) issueInstant.clone(); } + + /** + * Returns the SAML settings specified at construction time. + * + * @return the SAML settings + */ + protected Saml2Settings getSettings() { + return settings; + } } 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 38814ff7..39ba1ce0 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -534,4 +534,13 @@ public Calendar getIssueInstant() throws ValidationError { } else return issueInstant == null? null: (Calendar) issueInstant.clone(); } + + /** + * Returns the SAML settings specified at construction time. + * + * @return the SAML settings + */ + protected Saml2Settings getSettings() { + return settings; + } } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 42183441..67ec0b55 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -62,6 +62,11 @@ public class Metadata { */ private final Integer cacheDuration; + /** + * Settings data. + */ + private final Saml2Settings settings; + /** * Constructs the Metadata object. * @@ -72,6 +77,7 @@ public class Metadata { * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration, AttributeConsumingService attributeConsumingService) throws CertificateEncodingException { + this.settings = settings; this.validUntilTime = validUntilTime; this.attributeConsumingService = attributeConsumingService; this.cacheDuration = cacheDuration; @@ -102,7 +108,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings) throws CertificateEncodingException { - + this.settings = settings; this.validUntilTime = Calendar.getInstance(); this.validUntilTime.add(Calendar.DAY_OF_YEAR, N_DAYS_VALID_UNTIL); @@ -407,4 +413,13 @@ public static String signMetadata(String metadata, PrivateKey key, X509Certifica LOGGER.debug("Signed metadata --> " + signedMetadata); return signedMetadata; } + + /** + * Returns the SAML settings specified at construction time. + * + * @return the SAML settings + */ + protected Saml2Settings getSettings() { + return settings; + } } From ce574274c886104d40f5b8c0fbc002d388274458 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 6 Apr 2021 14:57:29 +0200 Subject: [PATCH 080/133] Fix typo in postProcessXml input parameter name --- .../main/java/com/onelogin/saml2/authn/AuthnRequest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index bd68ddea..c40b705f 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -129,14 +129,14 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv * this class have already been initialised. Its default implementation simply * returns the input XML as-is, with no change. * - * @param authRequestXml + * @param authnRequestXml * the XML produced for this AuthnRequest by the standard * implementation provided by {@link AuthnRequest} * @return the post-processed XML for this AuthnRequest, which will then be * returned by any call to {@link #getAuthnRequestXml()} */ - protected String postProcessXml(final String authRequestXml) { - return authRequestXml; + protected String postProcessXml(final String authnRequestXml) { + return authnRequestXml; } /** From 75acb1915a9df8a0a7987ab31f9e37c2504dc0b4 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 7 Apr 2021 15:26:10 +0200 Subject: [PATCH 081/133] Add missing call to postProcessXml in other Metadata constructor --- core/src/main/java/com/onelogin/saml2/settings/Metadata.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 67ec0b55..dc7a2524 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -83,7 +83,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu this.cacheDuration = cacheDuration; StrSubstitutor substitutor = generateSubstitutor(settings); - String unsignedMetadataString = substitutor.replace(getMetadataTemplate()); + String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate())); LOGGER.debug("metadata --> " + unsignedMetadataString); metadataString = unsignedMetadataString; From 23c12fb12dfe32801cfeaf5be739d52fb101f12e Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 23 Jul 2021 16:31:18 +0200 Subject: [PATCH 082/133] Revert "Add protected getter for settings to ease extension" This reverts commit 91fb55901b605a2b96468acd677fc71b8aec323a. --- .../com/onelogin/saml2/authn/AuthnRequest.java | 9 --------- .../com/onelogin/saml2/authn/SamlResponse.java | 17 ++++------------- .../onelogin/saml2/logout/LogoutRequest.java | 9 --------- .../onelogin/saml2/logout/LogoutResponse.java | 9 --------- .../com/onelogin/saml2/settings/Metadata.java | 17 +---------------- 5 files changed, 5 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index c40b705f..b9c23947 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -288,13 +288,4 @@ public String getId() public Calendar getIssueInstant() { return issueInstant == null? null: (Calendar) issueInstant.clone(); } - - /** - * Returns the SAML settings specified at construction time. - * - * @return the SAML settings - */ - protected Saml2Settings getSettings() { - return settings; - } } 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 364813d1..b3022acb 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -41,13 +41,13 @@ */ public class SamlResponse { /** - * Private property to construct a logger for this class. - */ + * Private property to construct a logger for this class. + */ private static final Logger LOGGER = LoggerFactory.getLogger(SamlResponse.class); /** - * Settings data. - */ + * Settings data. + */ private final Saml2Settings settings; /** @@ -1322,13 +1322,4 @@ public Calendar getResponseIssueInstant() throws ValidationError { } return result; } - - /** - * Returns the SAML settings specified at construction time. - * - * @return the SAML settings - */ - protected Saml2Settings getSettings() { - return settings; - } } 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 be30bf83..c1572e09 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -829,13 +829,4 @@ public String getId() public Calendar getIssueInstant() { return issueInstant == null? null: (Calendar) issueInstant.clone(); } - - /** - * Returns the SAML settings specified at construction time. - * - * @return the SAML settings - */ - protected Saml2Settings getSettings() { - return settings; - } } 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 39ba1ce0..38814ff7 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -534,13 +534,4 @@ public Calendar getIssueInstant() throws ValidationError { } else return issueInstant == null? null: (Calendar) issueInstant.clone(); } - - /** - * Returns the SAML settings specified at construction time. - * - * @return the SAML settings - */ - protected Saml2Settings getSettings() { - return settings; - } } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index dc7a2524..15056535 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -62,11 +62,6 @@ public class Metadata { */ private final Integer cacheDuration; - /** - * Settings data. - */ - private final Saml2Settings settings; - /** * Constructs the Metadata object. * @@ -77,7 +72,6 @@ public class Metadata { * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration, AttributeConsumingService attributeConsumingService) throws CertificateEncodingException { - this.settings = settings; this.validUntilTime = validUntilTime; this.attributeConsumingService = attributeConsumingService; this.cacheDuration = cacheDuration; @@ -108,7 +102,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu * @throws CertificateEncodingException */ public Metadata(Saml2Settings settings) throws CertificateEncodingException { - this.settings = settings; + this.validUntilTime = Calendar.getInstance(); this.validUntilTime.add(Calendar.DAY_OF_YEAR, N_DAYS_VALID_UNTIL); @@ -413,13 +407,4 @@ public static String signMetadata(String metadata, PrivateKey key, X509Certifica LOGGER.debug("Signed metadata --> " + signedMetadata); return signedMetadata; } - - /** - * Returns the SAML settings specified at construction time. - * - * @return the SAML settings - */ - protected Saml2Settings getSettings() { - return settings; - } } From 423618a86ee69d28ef6d030e1167135b7f141c0e Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 23 Jul 2021 16:48:07 +0200 Subject: [PATCH 083/133] Pass settings directly to postProcessXml and improve tests --- .../main/java/com/onelogin/saml2/authn/AuthnRequest.java | 8 +++++--- .../java/com/onelogin/saml2/logout/LogoutRequest.java | 6 ++++-- .../java/com/onelogin/saml2/logout/LogoutResponse.java | 6 ++++-- .../main/java/com/onelogin/saml2/settings/Metadata.java | 8 +++++--- .../com/onelogin/saml2/test/authn/AuthnRequestTest.java | 5 ++++- .../com/onelogin/saml2/test/logout/LogoutRequestTest.java | 5 ++++- .../onelogin/saml2/test/logout/LogoutResponseTest.java | 5 ++++- .../com/onelogin/saml2/test/settings/MetadataTest.java | 6 ++++-- 8 files changed, 34 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index b9c23947..1d03c749 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -11,8 +11,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.model.Organization; +import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.Util; @@ -101,7 +101,7 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv this.nameIdValueReq = nameIdValueReq; StrSubstitutor substitutor = generateSubstitutor(settings); - authnRequestString = postProcessXml(substitutor.replace(getAuthnRequestTemplate())); + authnRequestString = postProcessXml(substitutor.replace(getAuthnRequestTemplate()), settings); LOGGER.debug("AuthNRequest --> " + authnRequestString); } @@ -132,10 +132,12 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv * @param authnRequestXml * the XML produced for this AuthnRequest by the standard * implementation provided by {@link AuthnRequest} + * @param settings + * the settings * @return the post-processed XML for this AuthnRequest, which will then be * returned by any call to {@link #getAuthnRequestXml()} */ - protected String postProcessXml(final String authnRequestXml) { + protected String postProcessXml(final String authnRequestXml, final Saml2Settings settings) { return authnRequestXml; } 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 c1572e09..d09bc9de 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -139,7 +139,7 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, this.sessionIndex = sessionIndex; StrSubstitutor substitutor = generateSubstitutor(settings); - logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate())); + logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate()), settings); } else { logoutRequestString = Util.base64decodedInflated(samlLogoutRequest); Document doc = Util.loadXML(logoutRequestString); @@ -237,10 +237,12 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request) { * @param logoutRequestXml * the XML produced for this LogoutRequest by the standard * implementation provided by {@link LogoutRequest} + * @param settings + * the settings * @return the post-processed XML for this LogoutRequest, which will then be * returned by any call to {@link #getLogoutRequestXml()} */ - protected String postProcessXml(final String logoutRequestXml) { + protected String postProcessXml(final String logoutRequestXml, final Saml2Settings settings) { return logoutRequestXml; } 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 38814ff7..b05e70eb 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -364,7 +364,7 @@ public void build(String inResponseTo, String statusCode) { this.inResponseTo = inResponseTo; StrSubstitutor substitutor = generateSubstitutor(settings, statusCode); - this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate())); + this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate()), settings); } /** @@ -396,10 +396,12 @@ public void build() { * @param logoutResponseXml * the XML produced for this LogoutResponse by the standard * implementation provided by {@link LogoutResponse} + * @param settings + * the settings * @return the post-processed XML for this LogoutResponse, which will then be * returned by any call to {@link #getLogoutResponseXml()} */ - protected String postProcessXml(final String logoutResponseXml) { + protected String postProcessXml(final String logoutResponseXml, final Saml2Settings settings) { return logoutResponseXml; } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 15056535..ceb9181e 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -77,7 +77,7 @@ public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDu this.cacheDuration = cacheDuration; StrSubstitutor substitutor = generateSubstitutor(settings); - String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate())); + String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate()), settings); LOGGER.debug("metadata --> " + unsignedMetadataString); metadataString = unsignedMetadataString; @@ -109,7 +109,7 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { this.cacheDuration = SECONDS_CACHED; StrSubstitutor substitutor = generateSubstitutor(settings); - String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate())); + String unsignedMetadataString = postProcessXml(substitutor.replace(getMetadataTemplate()), settings); LOGGER.debug("metadata --> " + unsignedMetadataString); metadataString = unsignedMetadataString; @@ -126,10 +126,12 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { * @param metadataXml * the XML produced for this metadata instance by the standard * implementation provided by {@link Metadata} + * @param settings + * the settings * @return the post-processed XML for this metadata instance, which will then be * returned by any call to {@link #getMetadataString()} */ - protected String postProcessXml(final String metadataXml) { + protected String postProcessXml(final String metadataXml, final Saml2Settings settings) { return metadataXml; } diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 31fd4f55..9dabd362 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -395,7 +396,9 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); AuthnRequest authnRequest = new AuthnRequest(settings) { @Override - protected String postProcessXml(String authRequestXml) { + protected String postProcessXml(String authRequestXml, Saml2Settings sett) { + assertEquals(authRequestXml, super.postProcessXml(authRequestXml, sett)); + assertSame(settings, sett); return "changed"; } }; diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index 5ae2a196..8dcc728f 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -945,7 +946,9 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); LogoutRequest logoutRequest = new LogoutRequest(settings) { @Override - protected String postProcessXml(String authRequestXml) { + protected String postProcessXml(String authRequestXml, Saml2Settings sett) { + assertEquals(authRequestXml, super.postProcessXml(authRequestXml, sett)); + assertSame(settings, sett); return "changed"; } }; diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index d1f4c349..5f4244a7 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -704,7 +705,9 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); LogoutResponse logoutResponse = new LogoutResponse(settings, null) { @Override - protected String postProcessXml(String authRequestXml) { + protected String postProcessXml(String authRequestXml, Saml2Settings sett) { + assertEquals(authRequestXml, super.postProcessXml(authRequestXml, sett)); + assertSame(settings, sett); return "changed"; } }; diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java index 4075332f..16c20ad5 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java @@ -7,7 +7,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNull; - +import static org.junit.Assert.assertSame; import java.io.IOException; import java.security.GeneralSecurityException; @@ -533,7 +533,9 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); Metadata metadata = new Metadata(settings) { @Override - protected String postProcessXml(String authRequestXml) { + protected String postProcessXml(String authRequestXml, Saml2Settings sett) { + assertEquals(authRequestXml, super.postProcessXml(authRequestXml, sett)); + assertSame(settings, sett); return "changed"; } }; From 0d874b44eff0ee06e90857833f8f982ec3fd066b Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 23 Jul 2021 18:04:52 +0200 Subject: [PATCH 084/133] Update com.azure optional dependencies --- core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 77087d29..c3ebfee9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,13 +72,13 @@ com.azure azure-security-keyvault-keys - 4.2.8 + 4.3.0 true com.azure azure-identity - 1.3.0 + 1.3.3 true From f68055555d5749706c990b12d5a6b11d00d44b1f Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 23 Jul 2021 18:06:21 +0200 Subject: [PATCH 085/133] [maven-release-plugin] prepare release v2.7.0 --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index c3ebfee9..ea86f971 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.1-SNAPSHOT + 2.7.0 jar diff --git a/pom.xml b/pom.xml index 68340bef..3dbe0dda 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.1-SNAPSHOT + 2.7.0 pom OneLogin java-saml Toolkit Project @@ -158,7 +158,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - HEAD + v2.7.0 diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index b9378351..481e71a2 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.6.1-SNAPSHOT + 2.7.0 java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 7dd43d95..e8d2e8e8 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.1-SNAPSHOT + 2.7.0 java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 14dd363e..03e80165 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.6.1-SNAPSHOT + 2.7.0 jar From 6fe8eec6790129b12dd871b115611f260850db35 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 23 Jul 2021 18:06:30 +0200 Subject: [PATCH 086/133] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index ea86f971..405ea7f1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.0 + 2.7.1-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index 3dbe0dda..b4bee9bb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.0 + 2.7.1-SNAPSHOT pom OneLogin java-saml Toolkit Project @@ -158,7 +158,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - v2.7.0 + HEAD diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 481e71a2..6ca21609 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.7.0 + 2.7.1-SNAPSHOT java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index e8d2e8e8..4f532de0 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.0 + 2.7.1-SNAPSHOT java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 03e80165..841ee89c 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.0 + 2.7.1-SNAPSHOT jar From 636f17b200ff6da26c220126618161016930a2dc Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Sat, 24 Jul 2021 16:49:23 +0200 Subject: [PATCH 087/133] Polish AuthnRequest constructor and Auth.login overloadings Input parameters, which drive how the AuthnRequest should be generated, are now encapsulated into an AuthnRequestParams object which is far more extensible and help to polish the API of AuthnRequest constructors and Auth.login, which are crowded with overloadings and cannot be easily extended. Also, these input params are passed to postProcessXml: in this way, if an extension plans to use a specialization of AuthnRequestParams, it can access such a specialized instance also within a proper postProcessXml overriding. --- .../onelogin/saml2/authn/AuthnRequest.java | 99 ++++----- .../saml2/authn/AuthnRequestParams.java | 105 +++++++++ .../com/onelogin/saml2/settings/Metadata.java | 2 - .../saml2/test/authn/AuthnRequestTest.java | 21 +- .../main/java/com/onelogin/saml2/Auth.java | 200 ++++++++++++++---- .../com/onelogin/saml2/test/AuthTest.java | 22 +- 6 files changed, 337 insertions(+), 112 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index d57fad85..8de333ca 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -42,26 +42,6 @@ public class AuthnRequest { */ private final Saml2Settings settings; - /** - * When true the AuthNRequest will set the ForceAuthn='true' - */ - private final boolean forceAuthn; - - /** - * When true the AuthNRequest will set the IsPassive='true' - */ - private final boolean isPassive; - - /** - * When true the AuthNReuqest will set a nameIdPolicy - */ - private final boolean setNameIdPolicy; - - /** - * Indicates to the IdP the subject that should be authenticated - */ - private final String nameIdValueReq; - /** * Time stamp that indicates when the AuthNRequest was created */ @@ -72,56 +52,74 @@ public class AuthnRequest { * * @param settings * OneLogin_Saml2_Settings + * @see #AuthnRequest(Saml2Settings, AuthnRequestParams) */ public AuthnRequest(Saml2Settings settings) { - this(settings, false, false, true); + this(settings, new AuthnRequestParams(false, false, true)); } /** * Constructs the AuthnRequest object. * * @param settings - * OneLogin_Saml2_Settings + * OneLogin_Saml2_Settings * @param forceAuthn - * When true the AuthNReuqest will set the ForceAuthn='true' + * When true the AuthNReuqest will set the ForceAuthn='true' * @param isPassive - * When true the AuthNReuqest will set the IsPassive='true' + * When true the AuthNReuqest will set the IsPassive='true' * @param setNameIdPolicy - * When true the AuthNReuqest will set a nameIdPolicy + * When true the AuthNReuqest will set a nameIdPolicy * @param nameIdValueReq - * Indicates to the IdP the subject that should be authenticated + * Indicates to the IdP the subject that should be authenticated + * @deprecated use {@link #AuthnRequest(Saml2Settings, AuthnRequestParams)} with + * {@link AuthnRequestParams#AuthnRequestParams(boolean, boolean, boolean, String)} + * instead */ + @Deprecated public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, String nameIdValueReq) { - this.id = Util.generateUniqueID(settings.getUniqueIDPrefix()); - issueInstant = Calendar.getInstance(); - this.isPassive = isPassive; - this.settings = settings; - this.forceAuthn = forceAuthn; - this.setNameIdPolicy = setNameIdPolicy; - this.nameIdValueReq = nameIdValueReq; - - StrSubstitutor substitutor = generateSubstitutor(settings); - authnRequestString = postProcessXml(substitutor.replace(getAuthnRequestTemplate()), settings); - LOGGER.debug("AuthNRequest --> " + authnRequestString); + this(settings, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq)); } - + /** * Constructs the AuthnRequest object. * * @param settings - * OneLogin_Saml2_Settings + * OneLogin_Saml2_Settings * @param forceAuthn - * When true the AuthNReuqest will set the ForceAuthn='true' + * When true the AuthNReuqest will set the ForceAuthn='true' * @param isPassive - * When true the AuthNReuqest will set the IsPassive='true' + * When true the AuthNReuqest will set the IsPassive='true' * @param setNameIdPolicy - * When true the AuthNReuqest will set a nameIdPolicy + * When true the AuthNReuqest will set a nameIdPolicy + * @deprecated use {@link #AuthnRequest(Saml2Settings, AuthnRequestParams)} with + * {@link AuthnRequestParams#AuthnRequestParams(boolean, boolean, boolean)} + * instead */ + @Deprecated public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy) { this(settings, forceAuthn, isPassive, setNameIdPolicy, null); } /** + * Constructs the AuthnRequest object. + * + * @param settings + * OneLogin_Saml2_Settings + * @param params + * a set of authentication request input parameters that shape the + * request to create + */ + public AuthnRequest(Saml2Settings settings, AuthnRequestParams params) { + this.id = Util.generateUniqueID(settings.getUniqueIDPrefix()); + issueInstant = Calendar.getInstance(); + this.settings = settings; + + StrSubstitutor substitutor = generateSubstitutor(params, settings); + authnRequestString = postProcessXml(substitutor.replace(getAuthnRequestTemplate()), params, settings); + LOGGER.debug("AuthNRequest --> " + authnRequestString); + } + + /* * Allows for an extension class to post-process the AuthnRequest XML generated * for this request, in order to customize the result. *

@@ -132,15 +130,17 @@ public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassiv * @param authnRequestXml * the XML produced for this AuthnRequest by the standard * implementation provided by {@link AuthnRequest} + * @param params + * the authentication request input parameters * @param settings * the settings * @return the post-processed XML for this AuthnRequest, which will then be * returned by any call to {@link #getAuthnRequestXml()} */ - protected String postProcessXml(final String authnRequestXml, final Saml2Settings settings) { + protected String postProcessXml(final String authnRequestXml, final AuthnRequestParams params, final Saml2Settings settings) { return authnRequestXml; } - + /** * @return the base64 encoded unsigned AuthnRequest (deflated or not) * @@ -181,22 +181,24 @@ public String getAuthnRequestXml() { /** * Substitutes AuthnRequest variables within a string by values. * + * @param params + * the authentication request input parameters * @param settings * Saml2Settings object. Setting data * * @return the StrSubstitutor object of the AuthnRequest */ - private StrSubstitutor generateSubstitutor(Saml2Settings settings) { + private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Settings settings) { Map valueMap = new HashMap(); String forceAuthnStr = ""; - if (forceAuthn) { + if (params.isForceAuthn()) { forceAuthnStr = " ForceAuthn=\"true\""; } String isPassiveStr = ""; - if (isPassive) { + if (params.isPassive()) { isPassiveStr = " IsPassive=\"true\""; } @@ -211,6 +213,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { valueMap.put("destinationStr", destinationStr); String subjectStr = ""; + String nameIdValueReq = params.getNameIdValueReq(); if (nameIdValueReq != null && !nameIdValueReq.isEmpty()) { String nameIDFormat = settings.getSpNameIDFormat(); subjectStr = ""; @@ -221,7 +224,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { valueMap.put("subjectStr", subjectStr); String nameIDPolicyStr = ""; - if (setNameIdPolicy) { + if (params.isSetNameIdPolicy()) { String nameIDPolicyFormat = settings.getSpNameIDFormat(); if (settings.getWantNameIdEncrypted()) { nameIDPolicyFormat = Constants.NAMEID_ENCRYPTED; diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java new file mode 100644 index 00000000..6aec5a54 --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java @@ -0,0 +1,105 @@ +package com.onelogin.saml2.authn; + +/** + * Input parameters for a SAML 2 authentication request. + */ +public class AuthnRequestParams { + + /** + * When true the AuthNRequest will set the ForceAuthn='true' + */ + private final boolean forceAuthn; + /** + * When true the AuthNRequest will set the IsPassive='true' + */ + private final boolean isPassive; + /** + * When true the AuthNReuqest will set a nameIdPolicy + */ + private final boolean setNameIdPolicy; + /** + * Indicates to the IdP the subject that should be authenticated + */ + private final String nameIdValueReq; + + /** + * Create a set of authentication request input parameters. + * + * @param forceAuthn + * whether the ForceAuthn attribute should be set to + * true + * @param isPassive + * whether the isPassive attribute should be set to + * true + * @param setNameIdPolicy + * whether a NameIDPolicy should be set + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy) { + this(forceAuthn, isPassive, setNameIdPolicy, null); + } + + /** + * Create a set of authentication request input parameters. + * + * @param forceAuthn + * whether the ForceAuthn attribute should be set to + * true + * @param isPassive + * whether the isPassive attribute should be set to + * true + * @param setNameIdPolicy + * whether a NameIDPolicy should be set + * @param nameIdValueReq + * the subject that should be authenticated + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, String nameIdValueReq) { + this.forceAuthn = forceAuthn; + this.isPassive = isPassive; + this.setNameIdPolicy = setNameIdPolicy; + this.nameIdValueReq = nameIdValueReq; + } + + /** + * Create a set of authentication request input parameters, by copying them from + * another set. + * + * @param source + * the source set of authentication request input parameters + */ + protected AuthnRequestParams(AuthnRequestParams source) { + this.forceAuthn = source.isForceAuthn(); + this.isPassive = source.isPassive(); + this.setNameIdPolicy = source.isSetNameIdPolicy(); + this.nameIdValueReq = source.getNameIdValueReq(); + } + + /** + * @return whether the ForceAuthn attribute should be set to + * true + */ + protected boolean isForceAuthn() { + return forceAuthn; + } + + /** + * @return whether the isPassive attribute should be set to + * true + */ + protected boolean isPassive() { + return isPassive; + } + + /** + * @return whether a NameIDPolicy should be set + */ + protected boolean isSetNameIdPolicy() { + return setNameIdPolicy; + } + + /** + * @return the subject that should be authenticated + */ + protected String getNameIdValueReq() { + return nameIdValueReq; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index a4760097..5ed070a6 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -1,7 +1,5 @@ package com.onelogin.saml2.settings; -import static com.onelogin.saml2.util.Util.toXml; - import java.net.URL; import java.util.Arrays; import java.util.Calendar; diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 253c86c5..2d4e6e38 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -17,6 +17,7 @@ import org.junit.Test; import com.onelogin.saml2.authn.AuthnRequest; +import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Util; @@ -164,13 +165,13 @@ public void testForceAuthN() throws Exception { assertThat(authnRequestStr, containsString("")); settings = new SettingsBuilder().fromFile("config/config.emailaddressformat.properties").build(); - authnRequest = new AuthnRequest(settings, false, false, false, "testuser@example.com"); + authnRequest = new AuthnRequest(settings, new AuthnRequestParams(false, false, false, "testuser@example.com")); authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); assertThat(authnRequestStr, containsString(" parameters = new HashMap(); - return login(relayState, forceAuthn, isPassive, setNameIdPolicy, stay, - nameIdValueReq, parameters); + return login(relayState, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq), stay, + parameters); } /** @@ -386,44 +391,15 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo * * @throws IOException * @throws SettingsException + * @deprecated use {@link #login(String, AuthnRequestParams, Boolean, Map)} with + * {@link AuthnRequestParams#AuthnRequestParams(boolean, boolean, boolean, String)} + * instead */ + @Deprecated public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay, String nameIdValueReq, Map parameters) throws IOException, SettingsException { - AuthnRequest authnRequest = new AuthnRequest(settings, forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq); - - if (parameters == null) { - parameters = new HashMap(); - } - - String samlRequest = authnRequest.getEncodedAuthnRequest(); - - parameters.put("SAMLRequest", samlRequest); - - if (relayState == null) { - relayState = ServletUtils.getSelfRoutedURLNoQuery(request); - } - - if (!relayState.isEmpty()) { - parameters.put("RelayState", relayState); - } - - if (settings.getAuthnRequestsSigned()) { - String sigAlg = settings.getSignatureAlgorithm(); - String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg); - - parameters.put("SigAlg", sigAlg); - parameters.put("Signature", signature); - } - - String ssoUrl = getSSOurl(); - lastRequestId = authnRequest.getId(); - lastRequestIssueInstant = authnRequest.getIssueInstant(); - lastRequest = authnRequest.getAuthnRequestXml(); - - if (!stay) { - LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest); - } - return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay); + return login(relayState, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq), stay, + parameters); } /** @@ -454,10 +430,14 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo * * @throws IOException * @throws SettingsException + * @deprecated use {@link #login(String, AuthnRequestParams, Boolean)} with + * {@link AuthnRequestParams#AuthnRequestParams(boolean, boolean, boolean)} + * instead */ + @Deprecated public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy, Boolean stay) throws IOException, SettingsException { - return login(relayState, forceAuthn, isPassive, setNameIdPolicy, stay, null); + return login(relayState, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy), stay, null); } /** @@ -484,10 +464,14 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo * * @throws IOException * @throws SettingsException + * @deprecated use {@link #login(String, AuthnRequestParams)} with + * {@link AuthnRequestParams#AuthnRequestParams(boolean, boolean, boolean)} + * instead */ + @Deprecated public void login(String relayState, Boolean forceAuthn, Boolean isPassive, Boolean setNameIdPolicy) throws IOException, SettingsException { - login(relayState, forceAuthn, isPassive, setNameIdPolicy, false); + login(relayState, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy), false); } /** @@ -497,7 +481,20 @@ public void login(String relayState, Boolean forceAuthn, Boolean isPassive, Bool * @throws SettingsException */ public void login() throws IOException, SettingsException { - login(null, false, false, true); + login(null, new AuthnRequestParams(false, false, true)); + } + + /** + * Initiates the SSO process. + * + * @param authnRequestParams + * the authentication request input parameters + * + * @throws IOException + * @throws SettingsException + */ + public void login(AuthnRequestParams authnRequestParams) throws IOException, SettingsException { + login(null, authnRequestParams); } /** @@ -521,7 +518,128 @@ public void login() throws IOException, SettingsException { * @throws SettingsException */ public void login(String relayState) throws IOException, SettingsException { - login(relayState, false, false, true); + login(relayState, new AuthnRequestParams(false, false, true)); + } + + /** + * Initiates the SSO process. + * + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param authnRequestParams + * the authentication request input parameters + * + * @throws IOException + * @throws SettingsException + */ + public void login(String relayState, AuthnRequestParams authnRequestParams) throws IOException, SettingsException { + login(relayState, authnRequestParams, false); + } + + /** + * Initiates the SSO process. + * + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param authnRequestParams + * the authentication request input parameters + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * + * @return the SSO URL with the AuthNRequest if stay = True + * + * @throws IOException + * @throws SettingsException + */ + public String login(String relayState, AuthnRequestParams authnRequestParams, Boolean stay) throws IOException, SettingsException { + return login(relayState, authnRequestParams, stay, new HashMap<>()); + } + + /** + * Initiates the SSO process. + * + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param authnRequestParams + * the authentication request input parameters + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param parameters + * Use it to send extra parameters in addition to the AuthNRequest + * + * @return the SSO URL with the AuthNRequest if stay = True + * + * @throws IOException + * @throws SettingsException + */ + public String login(String relayState, AuthnRequestParams authnRequestParams, Boolean stay, Map parameters) throws IOException, SettingsException { + AuthnRequest authnRequest = new AuthnRequest(settings, authnRequestParams); + + if (parameters == null) { + parameters = new HashMap(); + } + + String samlRequest = authnRequest.getEncodedAuthnRequest(); + + parameters.put("SAMLRequest", samlRequest); + + if (relayState == null) { + relayState = ServletUtils.getSelfRoutedURLNoQuery(request); + } + + if (!relayState.isEmpty()) { + parameters.put("RelayState", relayState); + } + + if (settings.getAuthnRequestsSigned()) { + String sigAlg = settings.getSignatureAlgorithm(); + String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg); + + parameters.put("SigAlg", sigAlg); + parameters.put("Signature", signature); + } + + String ssoUrl = getSSOurl(); + lastRequestId = authnRequest.getId(); + lastRequestIssueInstant = authnRequest.getIssueInstant(); + lastRequest = authnRequest.getAuthnRequestXml(); + + if (!stay) { + LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest); + } + return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay); } /** 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 e0426f6c..b52e22b5 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -43,11 +43,14 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; +import org.w3c.dom.Document; import com.onelogin.saml2.Auth; +import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.exception.Error; -import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.settings.Saml2Settings; @@ -55,9 +58,6 @@ import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.Util; -import org.mockito.ArgumentCaptor; -import org.w3c.dom.Document; - public class AuthTest { @Rule @@ -1381,7 +1381,7 @@ public void testLoginWithExtraParameters() throws IOException, SettingsException Auth auth = new Auth(settings, request, response); Map extraParameters = new HashMap(); extraParameters.put("parameter1", "xxx"); - String target = auth.login("", false, false, false, true, null, extraParameters); + String target = auth.login("", new AuthnRequestParams(false, false, false), true, extraParameters); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php?SAMLRequest=")); assertThat(target, containsString("¶meter1=xxx")); } @@ -1410,12 +1410,12 @@ public void testLoginStay() throws IOException, SettingsException, URISyntaxExce settings.setAuthnRequestsSigned(false); Auth auth = new Auth(settings, request, response); - String target = auth.login("", false, false, false, true); + String target = auth.login("", new AuthnRequestParams(false, false, false), true); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php?SAMLRequest=")); assertThat(target, not(containsString("&RelayState="))); String relayState = "http://localhost:8080/expected.jsp"; - target = auth.login(relayState, false, false, false, true); + target = auth.login(relayState, new AuthnRequestParams(false, false, false), true); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php?SAMLRequest=")); assertThat(target, containsString("&RelayState=http%3A%2F%2Flocalhost%3A8080%2Fexpected.jsp")); } @@ -1443,13 +1443,13 @@ public void testLoginSubject() throws IOException, SettingsException, URISyntaxE Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); Auth auth = new Auth(settings, request, response); - String target = auth.login("", false, false, false, true); + String target = auth.login("", new AuthnRequestParams(false, false, false), true); assertThat(target, startsWith("http://idp.example.com/simplesaml/saml2/idp/SSOService.php?SAMLRequest=")); String authNRequestStr = getSAMLRequestFromURL(target); assertThat(authNRequestStr, containsString(" Date: Fri, 2 Apr 2021 17:28:52 +0200 Subject: [PATCH 088/133] Polish LogoutRequest constructors and Auth.logout overloadings This refactor is pretty much the same done with AuthnRequest and AuthnRequestParams: in this case, the params class encapsulates all the logout request input parameters used whenever a new logout request is created for subsequent sending. This as well allows to reduce the number of LogoutRequest constructor and Auth.logout() overloadings, allows an extension to use a customized input param object which is also passed to postProcessXml and eases Auth extensibility. --- .../onelogin/saml2/logout/LogoutRequest.java | 117 ++++++---- .../saml2/logout/LogoutRequestParams.java | 162 ++++++++++++++ .../saml2/test/logout/LogoutRequestTest.java | 46 ++-- .../main/java/com/onelogin/saml2/Auth.java | 207 ++++++++++++++---- .../com/onelogin/saml2/test/AuthTest.java | 9 +- 5 files changed, 429 insertions(+), 112 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java 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 f41c9266..d3e1d222 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -21,7 +21,6 @@ import org.w3c.dom.NodeList; import com.onelogin.saml2.exception.ValidationError; -import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.settings.Saml2Settings; @@ -60,31 +59,6 @@ public class LogoutRequest { */ private final HttpRequest request; - /** - * NameID. - */ - private String nameId; - - /** - * NameID Format. - */ - private String nameIdFormat; - - /** - * nameId NameQualifier - */ - private String nameIdNameQualifier; - - /** - * nameId SP NameQualifier - */ - private String nameIdSPNameQualifier; - - /** - * SessionIndex. When the user is logged, this stored it from the AuthnStatement of the SAML Response - */ - private String sessionIndex; - /** * URL of the current host + current view */ @@ -117,7 +91,14 @@ public class LogoutRequest { * The NameID NameQualifier that will be set in the LogoutRequest. * @param nameIdSPNameQualifier * The SP Name Qualifier that will be set in the LogoutRequest. + * + * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a + * received request from the HTTP request, or + * {@link #LogoutRequest(Saml2Settings, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String, String)} + * to build a new request to be sent */ + @Deprecated public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) { this.settings = settings; this.request = request; @@ -130,16 +111,12 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, } if (samlLogoutRequest == null) { + LogoutRequestParams params = new LogoutRequestParams(sessionIndex, nameId, nameIdFormat, nameIdNameQualifier, nameIdSPNameQualifier); id = Util.generateUniqueID(settings.getUniqueIDPrefix()); issueInstant = Calendar.getInstance(); - this.nameId = nameId; - this.nameIdFormat = nameIdFormat; - this.nameIdNameQualifier = nameIdNameQualifier; - this.nameIdSPNameQualifier = nameIdSPNameQualifier; - this.sessionIndex = sessionIndex; - StrSubstitutor substitutor = generateSubstitutor(settings); - logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate()), settings); + StrSubstitutor substitutor = generateSubstitutor(params, settings); + logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate()), params, settings); } else { logoutRequestString = Util.base64decodedInflated(samlLogoutRequest); Document doc = Util.loadXML(logoutRequestString); @@ -163,7 +140,14 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The nameIdFormat that will be set in the LogoutRequest. * @param nameIdNameQualifier * The NameID NameQualifier will be set in the LogoutRequest. + * + * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a + * received request from the HTTP request, or + * {@link #LogoutRequest(Saml2Settings, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String)} + * to build a new request to be sent */ + @Deprecated public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier) { this(settings, request, nameId, sessionIndex, nameIdFormat, nameIdNameQualifier, null); } @@ -181,7 +165,14 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The SessionIndex (taken from the SAML Response in the SSO process). * @param nameIdFormat * The nameIdFormat that will be set in the LogoutRequest. + * + * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a + * received request from the HTTP request, or + * {@link #LogoutRequest(Saml2Settings, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String)} + * to build a new request to be sent */ + @Deprecated public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat) { this(settings, request, nameId, sessionIndex, nameIdFormat, null); } @@ -197,23 +188,34 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * The NameID that will be set in the LogoutRequest. * @param sessionIndex * The SessionIndex (taken from the SAML Response in the SSO process). + * + * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a + * received request from the HTTP request, or + * {@link #LogoutRequest(Saml2Settings, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String)} + * to build a new request to be sent */ + @Deprecated public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex) { this(settings, request, nameId, sessionIndex, null); } /** - * Constructs the LogoutRequest object. + * Constructs a LogoutRequest object when a new request should be generated + * and sent. * * @param settings * OneLogin_Saml2_Settings + * + * @see #LogoutRequest(Saml2Settings, LogoutRequestParams) */ public LogoutRequest(Saml2Settings settings) { - this(settings, null, null, null); + this(settings, new LogoutRequestParams()); } /** - * Constructs the LogoutRequest object. + * Constructs the LogoutRequest object when a received request should be extracted + * from the HTTP request and parsed. * * @param settings * OneLogin_Saml2_Settings @@ -224,6 +226,26 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request) { this(settings, request, null, null); } + /** + * Constructs the LogoutRequest object when a new request should be generated + * and sent. + * + * @param settings + * OneLogin_Saml2_Settings + * @param params + * a set of authentication request input parameters that shape the + * request to create + */ + public LogoutRequest(Saml2Settings settings, LogoutRequestParams params) { + this.settings = settings; + this.request = null; + id = Util.generateUniqueID(settings.getUniqueIDPrefix()); + issueInstant = Calendar.getInstance(); + + StrSubstitutor substitutor = generateSubstitutor(params, settings); + logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate()), params, settings); + } + /** * Allows for an extension class to post-process the LogoutRequest XML generated * for this request, in order to customize the result. @@ -237,12 +259,14 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request) { * @param logoutRequestXml * the XML produced for this LogoutRequest by the standard * implementation provided by {@link LogoutRequest} + * @param params + * the logout request input parameters * @param settings * the settings * @return the post-processed XML for this LogoutRequest, which will then be * returned by any call to {@link #getLogoutRequestXml()} */ - protected String postProcessXml(final String logoutRequestXml, final Saml2Settings settings) { + protected String postProcessXml(final String logoutRequestXml, final LogoutRequestParams params, final Saml2Settings settings) { return logoutRequestXml; } @@ -286,12 +310,14 @@ public String getLogoutRequestXml() { /** * Substitutes LogoutRequest variables within a string by values. * + * @param params + * the logout request input parameters * @param settings - * Saml2Settings object. Setting data + * Saml2Settings object. Setting data * - * @return the StrSubstitutor object of the LogoutRequest + * @return the StrSubstitutor object of the LogoutRequest */ - private StrSubstitutor generateSubstitutor(Saml2Settings settings) { + private StrSubstitutor generateSubstitutor(LogoutRequestParams params, Saml2Settings settings) { Map valueMap = new HashMap(); valueMap.put("id", Util.toXml(id)); @@ -308,14 +334,16 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { valueMap.put("issuer", Util.toXml(settings.getSpEntityId())); + String nameId = params.getNameId(); + String requestedNameIdFormat = params.getNameIdFormat(); String nameIdFormat = null; - String spNameQualifier = this.nameIdSPNameQualifier; - String nameQualifier = this.nameIdNameQualifier; + String spNameQualifier = params.getNameIdSPNameQualifier(); + String nameQualifier = params.getNameIdNameQualifier(); if (nameId != null) { - if (this.nameIdFormat == null && !settings.getSpNameIDFormat().equals(Constants.NAMEID_UNSPECIFIED)) { + if (requestedNameIdFormat == null && !settings.getSpNameIDFormat().equals(Constants.NAMEID_UNSPECIFIED)) { nameIdFormat = settings.getSpNameIDFormat(); } else { - nameIdFormat = this.nameIdFormat; + nameIdFormat = requestedNameIdFormat; } } else { nameId = settings.getIdpEntityId(); @@ -348,6 +376,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) { valueMap.put("nameIdStr", nameIdStr); String sessionIndexStr = ""; + String sessionIndex = params.getSessionIndex(); if (sessionIndex != null) { sessionIndexStr = " " + Util.toXml(sessionIndex) + ""; } diff --git a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java new file mode 100644 index 00000000..ce95626f --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java @@ -0,0 +1,162 @@ +package com.onelogin.saml2.logout; + +/** + * Input parameters for a SAML 2 logout request. + */ +public class LogoutRequestParams { + + /** + * SessionIndex. When the user is logged, this stored it from the AuthnStatement + * of the SAML Response + */ + private String sessionIndex; + + /** + * NameID. + */ + private String nameId; + + /** + * NameID Format. + */ + private String nameIdFormat; + + /** + * nameId NameQualifier + */ + private String nameIdNameQualifier; + + /** + * nameId SP NameQualifier + */ + private String nameIdSPNameQualifier; + + /** Create an empty set of logout request input parameters. */ + public LogoutRequestParams() { + } + + /** + * Create a set of logout request input parameters. + * + * @param sessionIndex + * the session index + * @param nameId + * the name id of the user to log out + */ + public LogoutRequestParams(String sessionIndex, String nameId) { + this(sessionIndex, nameId, null, null, null); + } + + /** + * Create a set of logout request input parameters. + * + * @param sessionIndex + * the session index + * @param nameId + * the name id of the user to log out + * @param nameIdFormat + * the name id format + */ + public LogoutRequestParams(String sessionIndex, String nameId, String nameIdFormat) { + this(sessionIndex, nameId, nameIdFormat, null, null); + } + + /** + * Create a set of logout request input parameters. + * + * @param sessionIndex + * the session index + * @param nameId + * the name id of the user to log out + * @param nameIdFormat + * the name id format + * @param nameIdNameQualifier + * the name id qualifier + */ + public LogoutRequestParams(String sessionIndex, String nameId, String nameIdFormat, String nameIdNameQualifier) { + this(sessionIndex, nameId, nameIdFormat, nameIdNameQualifier, null); + } + + /** + * Create a set of logout request input parameters. + * + * @param sessionIndex + * the session index + * @param nameId + * the name id of the user to log out + * @param nameIdFormat + * the name id format + * @param nameIdNameQualifier + * the name id qualifier + * @param nameIdSPNameQualifier + * the name id SP qualifier + */ + public LogoutRequestParams(String sessionIndex, String nameId, String nameIdFormat, String nameIdNameQualifier, + String nameIdSPNameQualifier) { + this.sessionIndex = sessionIndex; + this.nameId = nameId; + this.nameIdFormat = nameIdFormat; + this.nameIdNameQualifier = nameIdNameQualifier; + this.nameIdSPNameQualifier = nameIdSPNameQualifier; + } + + /** + * Create a set of logout request input parameters, by copying them from another + * set. + * + * @param source + * the source set of logout request input parameters + */ + protected LogoutRequestParams(LogoutRequestParams source) { + this.sessionIndex = source.getSessionIndex(); + this.nameId = source.getNameId(); + this.nameIdFormat = source.getNameIdFormat(); + this.nameIdNameQualifier = source.getNameIdNameQualifier(); + this.nameIdSPNameQualifier = source.getNameIdSPNameQualifier(); + } + + /** + * @return the name ID + */ + protected String getNameId() { + return nameId; + } + + /** + * Sets the name ID + * + * @param nameId + * the name ID to set + */ + protected void setNameId(String nameId) { + this.nameId = nameId; + } + + /** + * @return the name ID format + */ + protected String getNameIdFormat() { + return nameIdFormat; + } + + /** + * @return the name ID name qualifier + */ + protected String getNameIdNameQualifier() { + return nameIdNameQualifier; + } + + /** + * @return the name ID SP name qualifier + */ + protected String getNameIdSPNameQualifier() { + return nameIdSPNameQualifier; + } + + /** + * @return the session index + */ + protected String getSessionIndex() { + return sessionIndex; + } +} \ No newline at end of file diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index 69f5a5f2..b3ae9c59 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -29,7 +29,7 @@ import org.junit.rules.ExpectedException; import com.onelogin.saml2.logout.LogoutRequest; -import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.logout.LogoutRequestParams; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.exception.Error; @@ -159,14 +159,14 @@ public void testConstructorWithRequest() throws Exception { public void testConstructorWithSessionIndex() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); String sessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf"; - LogoutRequest logoutRequest = new LogoutRequest(settings, null, null, sessionIndex); + LogoutRequest logoutRequest = new LogoutRequest(settings, new LogoutRequestParams(sessionIndex, null)); String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); String expectedSessionIndex = "_51be37965feb5579d803141076936dc2e9d1d98ebf"; assertThat(logoutRequestStr, containsString("", null); + LogoutRequest logoutRequest = new LogoutRequest(settings, new LogoutRequestParams(null, "")); String logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); assertThat(logoutRequestStr, containsString("<ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c>")); assertThat(logoutRequestStr, not(containsString("SPNameQualifier"))); - logoutRequest = new LogoutRequest(settings, null, null, null); + logoutRequest = new LogoutRequest(settings, new LogoutRequestParams(null, null)); logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest(); logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); assertThat(logoutRequestStr, containsString("null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param logoutRequestParams + * the logout request input parameters + * + * @return the SLO URL with the LogoutRequest if stay = True + * + * @throws IOException + * @throws SettingsException + */ + public String logout(String relayState, LogoutRequestParams logoutRequestParams, Boolean stay) + throws IOException, SettingsException { + Map parameters = new HashMap(); + return logout(relayState, logoutRequestParams, stay, parameters); + } + + /** + * Initiates the SLO process. + * + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param logoutRequestParams + * the logout request input parameters + * + * @throws IOException + * @throws SettingsException + */ + public void logout(String relayState, LogoutRequestParams logoutRequestParams) + throws IOException, SettingsException { + logout(relayState, logoutRequestParams, false); + } + /** * Initiates the SLO process. * @@ -675,60 +734,53 @@ public String login(String relayState, AuthnRequestParams authnRequestParams, Bo * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String, String)} + * instead */ public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) throws IOException, SettingsException { Map parameters = new HashMap(); - return logout(relayState, nameId, sessionIndex, stay, nameidFormat, - nameIdNameQualifier, nameIdSPNameQualifier, parameters); + return logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier), stay, parameters); } /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the - * LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response - * in the SSO process). - * @param stay True if we want to stay (returns the url string) - * False to execute redirection - * @param nameidFormat The NameID Format that will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier that will be set in the - * LogoutRequest. - * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in - * the LogoutRequest. - * @param parameters Use it to send extra parameters in addition to the LogoutRequest + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param logoutRequestParams + * the logout request input parameters + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param parameters + * Use it to send extra parameters in addition to the LogoutRequest * * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException * @throws SettingsException */ - public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, - String nameIdNameQualifier, String nameIdSPNameQualifier, Map parameters) + public String logout(String relayState, LogoutRequestParams logoutRequestParams, Boolean stay, Map parameters) throws IOException, SettingsException { if (parameters == null) { parameters = new HashMap(); } - LogoutRequest logoutRequest = new LogoutRequest(settings, null, nameId, sessionIndex, nameidFormat, - nameIdNameQualifier, nameIdSPNameQualifier); + LogoutRequest logoutRequest = new LogoutRequest(settings, logoutRequestParams); String samlLogoutRequest = logoutRequest.getEncodedLogoutRequest(); parameters.put("SAMLRequest", samlLogoutRequest); @@ -759,6 +811,51 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool return ServletUtils.sendRedirect(response, sloUrl, parameters, stay); } + /** + * Initiates the SLO process. + * + * @param relayState a state information to pass forth and back between + * the Service Provider and the Identity Provider; + * in the most simple case, it may be a URL to which + * the logged out user should be redirected after the + * logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for + * this relayState data and that protection strategies + * against tampering should better be implemented; + * it will be a self-routed URL when null, + * otherwise no relayState at all will be appended if an empty + * string is provided + * @param nameId The NameID that will be set in the + * LogoutRequest. + * @param sessionIndex The SessionIndex (taken from the SAML Response + * in the SSO process). + * @param stay True if we want to stay (returns the url string) + * False to execute redirection + * @param nameidFormat The NameID Format that will be set in the + * LogoutRequest. + * @param nameIdNameQualifier The NameID NameQualifier that will be set in the + * LogoutRequest. + * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in + * the LogoutRequest. + * @param parameters Use it to send extra parameters in addition to the LogoutRequest + * + * @return the SLO URL with the LogoutRequest if stay = True + * + * @throws IOException + * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean, Map)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String, String)} + * instead + */ + @Deprecated + public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, + String nameIdNameQualifier, String nameIdSPNameQualifier, Map parameters) + throws IOException, SettingsException { + return logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier), stay, parameters); + } + /** * Initiates the SLO process. * @@ -789,10 +886,14 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String)} + * instead */ + @Deprecated public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat, String nameIdNameQualifier) throws IOException, SettingsException { - return logout(relayState, nameId, sessionIndex, stay, nameidFormat, nameIdNameQualifier, null); + return logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat, nameIdNameQualifier), stay, null); } /** @@ -822,10 +923,14 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String)} + * instead */ + @Deprecated public String logout(String relayState, String nameId, String sessionIndex, Boolean stay, String nameidFormat) throws IOException, SettingsException { - return logout(relayState, nameId, sessionIndex, stay, nameidFormat, null); + return logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat), stay, null); } /** @@ -854,10 +959,14 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String)} + * instead */ + @Deprecated public String logout(String relayState, String nameId, String sessionIndex, Boolean stay) throws IOException, SettingsException { - return logout(relayState, nameId, sessionIndex, stay, null); + return logout(relayState, new LogoutRequestParams(sessionIndex, nameId), stay, null); } /** @@ -888,11 +997,15 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool * the LogoutRequest. * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String, String)} + * instead */ + @Deprecated public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) throws IOException, SettingsException { - logout(relayState, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier); + logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat, nameIdNameQualifier, nameIdSPNameQualifier), false); } /** @@ -921,10 +1034,14 @@ public void logout(String relayState, String nameId, String sessionIndex, String * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String)} + * instead */ + @Deprecated public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat, String nameIdNameQualifier) throws IOException, SettingsException { - logout(relayState, nameId, sessionIndex, false, nameidFormat, nameIdNameQualifier); + logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat, nameIdNameQualifier), false); } /** @@ -949,10 +1066,14 @@ public void logout(String relayState, String nameId, String sessionIndex, String * @param nameidFormat The NameID Format will be set in the LogoutRequest. * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String, String)} + * instead */ + @Deprecated public void logout(String relayState, String nameId, String sessionIndex, String nameidFormat) throws IOException, SettingsException { - logout(relayState, nameId, sessionIndex, false, nameidFormat); + logout(relayState, new LogoutRequestParams(sessionIndex, nameId, nameidFormat), false); } /** @@ -977,10 +1098,14 @@ public void logout(String relayState, String nameId, String sessionIndex, String * * @throws IOException * @throws SettingsException + * @deprecated use {@link #logout(String, LogoutRequestParams)} with + * {@link LogoutRequestParams#LogoutRequestParams(String, String)} + * instead */ + @Deprecated public void logout(String relayState, String nameId, String sessionIndex) throws IOException, SettingsException { - logout(relayState, nameId, sessionIndex, false, null); + logout(relayState, new LogoutRequestParams(sessionIndex, nameId), false, null); } /** @@ -990,7 +1115,7 @@ public void logout(String relayState, String nameId, String sessionIndex) * @throws SettingsException */ public void logout() throws IOException, SettingsException { - logout(null, null, null, false); + logout(null, new LogoutRequestParams(), false); } /** @@ -1014,7 +1139,7 @@ public void logout() throws IOException, SettingsException { * @throws SettingsException */ public void logout(String relayState) throws IOException, SettingsException { - logout(relayState, null, null); + logout(relayState, new LogoutRequestParams(), false); } /** 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 b52e22b5..ff8f1e54 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -52,6 +52,7 @@ import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.XMLEntityException; +import com.onelogin.saml2.logout.LogoutRequestParams; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -1586,7 +1587,7 @@ public void testLogoutWithExtraParameters() throws IOException, SettingsExceptio Auth auth = new Auth(settings, request, response); Map extraParameters = new HashMap(); extraParameters.put("parameter1", "xxx"); - String target = auth.logout("", null, null, true, null, null, null, extraParameters); + String target = auth.logout("", new LogoutRequestParams(), true, extraParameters); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php?SAMLRequest=")); assertThat(target, containsString("¶meter1=xxx")); } @@ -1677,12 +1678,12 @@ public void testLogoutStay() throws IOException, SettingsException, XMLEntityExc settings.setLogoutRequestSigned(false); Auth auth = new Auth(settings, request, response); - String target = auth.logout("", null, null, true); + String target = auth.logout("", new LogoutRequestParams(), true); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php?SAMLRequest=")); assertThat(target, not(containsString("&RelayState="))); String relayState = "http://localhost:8080/expected.jsp"; - target = auth.logout(relayState, null, null, true); + target = auth.logout(relayState, new LogoutRequestParams(), true); assertThat(target, startsWith("https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php?SAMLRequest=")); assertThat(target, containsString("&RelayState=http%3A%2F%2Flocalhost%3A8080%2Fexpected.jsp")); } @@ -2112,7 +2113,7 @@ public void testGetLastLogoutRequestSent() throws IOException, SettingsException Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); Auth auth = new Auth(settings, request, response); - String targetSLOURL = auth.logout(null, null, null, true); + String targetSLOURL = auth.logout(null, new LogoutRequestParams(), true); String logoutRequestXML = auth.getLastRequestXML(); assertThat(targetSLOURL, containsString(Util.urlEncoder(Util.deflatedBase64encoded(logoutRequestXML)))); From a777cdebeea24a1ca03a5b998d3f90b7568f5a17 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Sat, 24 Jul 2021 17:36:26 +0200 Subject: [PATCH 089/133] Fix postProcessXml overriding string parameter name --- .../com/onelogin/saml2/test/logout/LogoutResponseTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index 5872c087..cd964bac 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -758,8 +758,8 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); LogoutResponse logoutResponse = new LogoutResponse(settings, null) { @Override - protected String postProcessXml(String authRequestXml, Saml2Settings sett) { - assertEquals(authRequestXml, super.postProcessXml(authRequestXml, sett)); + protected String postProcessXml(String logoutResponseXml, Saml2Settings sett) { + assertEquals(logoutResponseXml, super.postProcessXml(logoutResponseXml, sett)); assertSame(settings, sett); return "changed"; } From b6f8ad3dba8197b4d78df464f23ba2acbdbb9297 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Sat, 24 Jul 2021 18:11:07 +0200 Subject: [PATCH 090/133] Reformat all overloadings javadoc for consistency --- .../onelogin/saml2/logout/LogoutRequest.java | 45 +- .../main/java/com/onelogin/saml2/Auth.java | 612 +++++++++--------- 2 files changed, 343 insertions(+), 314 deletions(-) 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 d3e1d222..234b89ac 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -80,17 +80,19 @@ public class LogoutRequest { * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...). * @param nameId * The NameID that will be set in the LogoutRequest. * @param sessionIndex - * The SessionIndex (taken from the SAML Response in the SSO process). + * The SessionIndex (taken from the SAML Response in the SSO + * process). * @param nameIdFormat * The nameIdFormat that will be set in the LogoutRequest. * @param nameIdNameQualifier - * The NameID NameQualifier that will be set in the LogoutRequest. + * The NameID NameQualifier that will be set in the LogoutRequest. * @param nameIdSPNameQualifier - * The SP Name Qualifier that will be set in the LogoutRequest. + * The SP Name Qualifier that will be set in the LogoutRequest. * * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a * received request from the HTTP request, or @@ -131,15 +133,17 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...). * @param nameId * The NameID that will be set in the LogoutRequest. * @param sessionIndex - * The SessionIndex (taken from the SAML Response in the SSO process). + * The SessionIndex (taken from the SAML Response in the SSO + * process). * @param nameIdFormat * The nameIdFormat that will be set in the LogoutRequest. * @param nameIdNameQualifier - * The NameID NameQualifier will be set in the LogoutRequest. + * The NameID NameQualifier will be set in the LogoutRequest. * * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a * received request from the HTTP request, or @@ -158,11 +162,13 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...). * @param nameId * The NameID that will be set in the LogoutRequest. * @param sessionIndex - * The SessionIndex (taken from the SAML Response in the SSO process). + * The SessionIndex (taken from the SAML Response in the SSO + * process). * @param nameIdFormat * The nameIdFormat that will be set in the LogoutRequest. * @@ -183,11 +189,13 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...). * @param nameId * The NameID that will be set in the LogoutRequest. * @param sessionIndex - * The SessionIndex (taken from the SAML Response in the SSO process). + * The SessionIndex (taken from the SAML Response in the SSO + * process). * * @deprecated use {@link #LogoutRequest(Saml2Settings, HttpRequest)} to build a * received request from the HTTP request, or @@ -201,11 +209,11 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, } /** - * Constructs a LogoutRequest object when a new request should be generated - * and sent. + * Constructs a LogoutRequest object when a new request should be generated and + * sent. * * @param settings - * OneLogin_Saml2_Settings + * OneLogin_Saml2_Settings * * @see #LogoutRequest(Saml2Settings, LogoutRequestParams) */ @@ -214,13 +222,14 @@ public LogoutRequest(Saml2Settings settings) { } /** - * Constructs the LogoutRequest object when a received request should be extracted - * from the HTTP request and parsed. + * Constructs the LogoutRequest object when a received request should be + * extracted from the HTTP request and parsed. * * @param settings - * OneLogin_Saml2_Settings + * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...). */ public LogoutRequest(Saml2Settings settings, HttpRequest request) { this(settings, request, null, null); diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 7b1f9002..dac4e4e3 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -322,28 +322,29 @@ public void setStrict(Boolean value) { /** * Initiates the SSO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the authenticated user should be redirected after the - * authentication response has been received back from the - * Identity Provider and validated correctly with - * {@link #processResponse()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param forceAuthn When true the AuthNRequest will set the - * ForceAuthn='true' - * @param isPassive When true the AuthNRequest will set the - * IsPassive='true' - * @param setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy - * @param stay True if we want to stay (returns the url string) False - * to execute redirection - * @param nameIdValueReq Indicates to the IdP the subject that should be - * authenticated + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param forceAuthn + * When true the AuthNRequest will set the ForceAuthn='true' + * @param isPassive + * When true the AuthNRequest will set the IsPassive='true' + * @param setNameIdPolicy + * When true the AuthNRequest will set a nameIdPolicy + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameIdValueReq + * Indicates to the IdP the subject that should be authenticated * * @return the SSO URL with the AuthNRequest if stay = True * @@ -364,29 +365,31 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo /** * Initiates the SSO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the authenticated user should be redirected after the - * authentication response has been received back from the - * Identity Provider and validated correctly with - * {@link #processResponse()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param forceAuthn When true the AuthNRequest will set the - * ForceAuthn='true' - * @param isPassive When true the AuthNRequest will set the - * IsPassive='true' - * @param setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy - * @param stay True if we want to stay (returns the url string) False - * to execute redirection - * @param nameIdValueReq Indicates to the IdP the subject that should be - * authenticated - * @param parameters Use it to send extra parameters in addition to the AuthNRequest + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param forceAuthn + * When true the AuthNRequest will set the ForceAuthn='true' + * @param isPassive + * When true the AuthNRequest will set the IsPassive='true' + * @param setNameIdPolicy + * When true the AuthNRequest will set a nameIdPolicy + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameIdValueReq + * Indicates to the IdP the subject that should be authenticated + * @param parameters + * Use it to send extra parameters in addition to the AuthNRequest * * @return the SSO URL with the AuthNRequest if stay = True * @@ -406,26 +409,27 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo /** * Initiates the SSO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the authenticated user should be redirected after the - * authentication response has been received back from the - * Identity Provider and validated correctly with - * {@link #processResponse()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param forceAuthn When true the AuthNRequest will set the - * ForceAuthn='true' - * @param isPassive When true the AuthNRequest will set the - * IsPassive='true' - * @param setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy - * @param stay True if we want to stay (returns the url string) False - * to execute redirection + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param forceAuthn + * When true the AuthNRequest will set the ForceAuthn='true' + * @param isPassive + * When true the AuthNRequest will set the IsPassive='true' + * @param setNameIdPolicy + * When true the AuthNRequest will set a nameIdPolicy + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection * * @return the SSO URL with the AuthNRequest if stay = True * @@ -444,24 +448,24 @@ public String login(String relayState, Boolean forceAuthn, Boolean isPassive, Bo /** * Initiates the SSO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the authenticated user should be redirected after the - * authentication response has been received back from the - * Identity Provider and validated correctly with - * {@link #processResponse()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param forceAuthn When true the AuthNRequest will set the - * ForceAuthn='true' - * @param isPassive When true the AuthNRequest will set the - * IsPassive='true' - * @param setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param forceAuthn + * When true the AuthNRequest will set the ForceAuthn='true' + * @param isPassive + * When true the AuthNRequest will set the IsPassive='true' + * @param setNameIdPolicy + * When true the AuthNRequest will set a nameIdPolicy * * @throws IOException * @throws SettingsException @@ -501,19 +505,18 @@ public void login(AuthnRequestParams authnRequestParams) throws IOException, Set /** * Initiates the SSO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the authenticated user should be redirected after the - * authentication response has been received back from the - * Identity Provider and validated correctly with - * {@link #processResponse()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the authenticated user should be + * redirected after the authentication response has been received + * back from the Identity Provider and validated correctly with + * {@link #processResponse()}; please note that SAML 2.0 + * specification imposes a limit of max 80 characters for this + * relayState data and that protection strategies against tampering + * should better be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided * * @throws IOException * @throws SettingsException @@ -704,31 +707,33 @@ public void logout(String relayState, LogoutRequestParams logoutRequestParams) /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the - * LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response - * in the SSO process). - * @param stay True if we want to stay (returns the url string) - * False to execute redirection - * @param nameidFormat The NameID Format that will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier that will be set in the - * LogoutRequest. - * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in - * the LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameidFormat + * The NameID Format that will be set in the LogoutRequest. + * @param nameIdNameQualifier + * The NameID NameQualifier that will be set in the LogoutRequest. + * @param nameIdSPNameQualifier + * The NameID SP Name Qualifier that will be set in the + * LogoutRequest. * * @return the SLO URL with the LogoutRequest if stay = True * @@ -814,38 +819,42 @@ public String logout(String relayState, LogoutRequestParams logoutRequestParams, /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the - * LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response - * in the SSO process). - * @param stay True if we want to stay (returns the url string) - * False to execute redirection - * @param nameidFormat The NameID Format that will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier that will be set in the - * LogoutRequest. - * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in - * the LogoutRequest. - * @param parameters Use it to send extra parameters in addition to the LogoutRequest + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameidFormat + * The NameID Format that will be set in the LogoutRequest. + * @param nameIdNameQualifier + * The NameID NameQualifier that will be set in the LogoutRequest. + * @param nameIdSPNameQualifier + * The NameID SP Name Qualifier that will be set in the + * LogoutRequest. + * @param parameters + * Use it to send extra parameters in addition to the LogoutRequest * * @return the SLO URL with the LogoutRequest if stay = True * * @throws IOException * @throws SettingsException - * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean, Map)} with + * @deprecated use {@link #logout(String, LogoutRequestParams, Boolean, Map)} + * with * {@link LogoutRequestParams#LogoutRequestParams(String, String, String, String, String)} * instead */ @@ -859,28 +868,30 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in - * the SSO process). - * @param stay True if we want to stay (returns the url string) - * False to execute redirection - * @param nameidFormat The NameID Format will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier will be set in the - * LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameidFormat + * The NameID Format will be set in the LogoutRequest. + * @param nameIdNameQualifier + * The NameID NameQualifier will be set in the LogoutRequest. * * @return the SLO URL with the LogoutRequest if stay = True * @@ -899,25 +910,28 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO - * process). - * @param stay True if we want to stay (returns the url string) False to - * execute redirection - * @param nameidFormat The NameID Format will be set in the LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection + * @param nameidFormat + * The NameID Format will be set in the LogoutRequest. * * @return the SLO URL with the LogoutRequest if stay = True * @@ -936,24 +950,26 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO - * process). - * @param stay True if we want to stay (returns the url string) False to - * execute redirection + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param stay + * True if we want to stay (returns the url string) False to + * execute redirection * * @return the SLO URL with the LogoutRequest if stay = True * @@ -972,29 +988,30 @@ public String logout(String relayState, String nameId, String sessionIndex, Bool /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the - * LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response - * in the SSO process). - * @param nameidFormat The NameID Format will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier that will be set in the - * LogoutRequest. - * @param nameIdSPNameQualifier The NameID SP Name Qualifier that will be set in - * the LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param nameidFormat + * The NameID Format will be set in the LogoutRequest. + * @param nameIdNameQualifier + * The NameID NameQualifier that will be set in the LogoutRequest. + * @param nameIdSPNameQualifier + * The NameID SP Name Qualifier that will be set in the + * LogoutRequest. * @throws IOException * @throws SettingsException * @deprecated use {@link #logout(String, LogoutRequestParams)} with @@ -1011,26 +1028,27 @@ public void logout(String relayState, String nameId, String sessionIndex, String /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in - * the SSO process). - * @param nameidFormat The NameID Format will be set in the - * LogoutRequest. - * @param nameIdNameQualifier The NameID NameQualifier will be set in the - * LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param nameidFormat + * The NameID Format will be set in the LogoutRequest. + * @param nameIdNameQualifier + * The NameID NameQualifier will be set in the LogoutRequest. * * @throws IOException * @throws SettingsException @@ -1047,23 +1065,25 @@ public void logout(String relayState, String nameId, String sessionIndex, String /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO - * process). - * @param nameidFormat The NameID Format will be set in the LogoutRequest. + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). + * @param nameidFormat + * The NameID Format will be set in the LogoutRequest. * @throws IOException * @throws SettingsException * @deprecated use {@link #logout(String, LogoutRequestParams)} with @@ -1079,22 +1099,23 @@ public void logout(String relayState, String nameId, String sessionIndex, String /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided - * @param nameId The NameID that will be set in the LogoutRequest. - * @param sessionIndex The SessionIndex (taken from the SAML Response in the SSO - * process). + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided + * @param nameId + * The NameID that will be set in the LogoutRequest. + * @param sessionIndex + * The SessionIndex (taken from the SAML Response in the SSO + * process). * * @throws IOException * @throws SettingsException @@ -1121,19 +1142,18 @@ public void logout() throws IOException, SettingsException { /** * Initiates the SLO process. * - * @param relayState a state information to pass forth and back between - * the Service Provider and the Identity Provider; - * in the most simple case, it may be a URL to which - * the logged out user should be redirected after the - * logout response has been received back from the - * Identity Provider and validated correctly with - * {@link #processSLO()}; please note that SAML 2.0 - * specification imposes a limit of max 80 characters for - * this relayState data and that protection strategies - * against tampering should better be implemented; - * it will be a self-routed URL when null, - * otherwise no relayState at all will be appended if an empty - * string is provided + * @param relayState + * a state information to pass forth and back between the Service + * Provider and the Identity Provider; in the most simple case, it + * may be a URL to which the logged out user should be redirected + * after the logout response has been received back from the + * Identity Provider and validated correctly with + * {@link #processSLO()}; please note that SAML 2.0 specification + * imposes a limit of max 80 characters for this relayState data + * and that protection strategies against tampering should better + * be implemented; it will be a self-routed URL when + * null, otherwise no relayState at all will be + * appended if an empty string is provided * * @throws IOException * @throws SettingsException From 8560b196029e0fdbb12ba9f217f13f15e9059a7a Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Sat, 24 Jul 2021 18:25:38 +0200 Subject: [PATCH 091/133] Fix postProcessXml overriding string parameter name in test and javadoc --- core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java | 2 +- .../java/com/onelogin/saml2/test/authn/AuthnRequestTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index 8de333ca..da64ce46 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -119,7 +119,7 @@ public AuthnRequest(Saml2Settings settings, AuthnRequestParams params) { LOGGER.debug("AuthNRequest --> " + authnRequestString); } - /* + /** * Allows for an extension class to post-process the AuthnRequest XML generated * for this request, in order to customize the result. *

diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 2d4e6e38..3212fb4c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -524,8 +524,8 @@ public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); AuthnRequest authnRequest = new AuthnRequest(settings) { @Override - protected String postProcessXml(String authRequestXml, AuthnRequestParams params, Saml2Settings sett) { - assertEquals(authRequestXml, super.postProcessXml(authRequestXml, params, sett)); + protected String postProcessXml(String authnRequestXml, AuthnRequestParams params, Saml2Settings sett) { + assertEquals(authnRequestXml, super.postProcessXml(authnRequestXml, params, sett)); assertSame(settings, sett); return "changed"; } From 07044ad1ee9d385282f289be91f307416acd1d1a Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 20 Apr 2021 09:46:32 +0200 Subject: [PATCH 092/133] Support more complex response statuses in LogoutResponse generation An overload of build() has been added to supply a SamlResponseStatus, so that it's possible to specify a more complex response status in the generated LogoutResponse. A typical example is the case of a partial logout, which needs a top-level urn:oasis:names:tc:SAML:2.0:status:Responder status code and a second-level urn:oasis:names:tc:SAML:2.0:status:PartialLogout one. Also, a status message can also be added now. Closes #342. --- .../onelogin/saml2/logout/LogoutResponse.java | 96 +++++++++++-------- .../saml2/test/logout/LogoutResponseTest.java | 27 +++++- 2 files changed, 81 insertions(+), 42 deletions(-) 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 9b724778..6deb9ee2 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -353,37 +353,48 @@ protected NodeList query (String query) throws XPathExpressionException { return Util.query(this.logoutResponseDocument, query, null); } - /** - * Generates a Logout Response XML string. - * - * @param inResponseTo - * InResponseTo attribute value to bet set at the Logout Response. - * @param statusCode - * String StatusCode to be set on the LogoutResponse - */ - public void build(String inResponseTo, String statusCode) { + /** + * Generates a Logout Response XML string. + * + * @param inResponseTo + * InResponseTo attribute value to bet set at the Logout Response. + * @param responseStatus + * SamlResponseStatus response status to be set on the LogoutResponse + */ + public void build(String inResponseTo, SamlResponseStatus responseStatus) { id = Util.generateUniqueID(settings.getUniqueIDPrefix()); issueInstant = Calendar.getInstance(); this.inResponseTo = inResponseTo; - StrSubstitutor substitutor = generateSubstitutor(settings, statusCode); + StrSubstitutor substitutor = generateSubstitutor(settings, responseStatus); this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate()), settings); } - /** - * Generates a Logout Response XML string. - * - * @param inResponseTo - * InResponseTo attribute value to bet set at the Logout Response. - */ + /** + * Generates a Logout Response XML string. + * + * @param inResponseTo + * InResponseTo attribute value to bet set at the Logout Response. + * @param statusCode + * String StatusCode to be set on the LogoutResponse + */ + public void build(String inResponseTo, String statusCode) { + build(inResponseTo, new SamlResponseStatus(statusCode)); + } + + /** + * Generates a Logout Response XML string. + * + * @param inResponseTo + * InResponseTo attribute value to bet set at the Logout Response. + */ public void build(String inResponseTo) { build(inResponseTo, Constants.STATUS_SUCCESS); } - /** - * Generates a Logout Response XML string. - * - */ + /** + * Generates a Logout Response XML string. + */ public void build() { build(null); } @@ -413,12 +424,12 @@ protected String postProcessXml(final String logoutResponseXml, final Saml2Setti * * @param settings * Saml2Settings object. Setting data - * @param statusCode - * String StatusCode to be set on the LogoutResponse + * @param responseStatus + * SamlResponseStatus response status to be set on the LogoutResponse * * @return the StrSubstitutor object of the LogoutResponse */ - private StrSubstitutor generateSubstitutor(Saml2Settings settings, String statusCode) { + private StrSubstitutor generateSubstitutor(Saml2Settings settings, SamlResponseStatus responseStatus) { Map valueMap = new HashMap(); valueMap.put("id", Util.toXml(id)); @@ -439,29 +450,34 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings, String status } valueMap.put("inResponseStr", inResponseStr); - String statusStr = ""; - if (statusCode != null) { - statusStr = "Value=\"" + Util.toXml(statusCode) + "\""; + StringBuilder statusStr = new StringBuilder(""); + } else { + statusStr.append(" />"); + } + String statusMessage = responseStatus.getStatusMessage(); + if (statusMessage != null) { + statusStr.append("") + .append(Util.toXml(statusMessage)) + .append(""); + } + } } - valueMap.put("statusStr", statusStr); + valueMap.put("statusStr", statusStr.toString()); valueMap.put("issuer", Util.toXml(settings.getSpEntityId())); return new StrSubstitutor(valueMap); } - /** - * Substitutes LogoutResponse variables within a string by values. - * - * @param settings - * Saml2Settings object. Setting data - * - * @return the StrSubstitutor object of the LogoutResponse - */ - private StrSubstitutor generateSubstitutor(Saml2Settings settings) { - return generateSubstitutor(settings, Constants.STATUS_SUCCESS); - } - /** * @return the LogoutResponse's template */ @@ -473,7 +489,7 @@ private static StringBuilder getLogoutResponseTemplate() { template.append("IssueInstant=\"${issueInstant}\"${destinationStr}${inResponseStr} >"); template.append("${issuer}"); template.append(""); - template.append(""); + template.append("${statusStr}"); template.append(""); template.append(""); return template; diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index 5872c087..e2e02c03 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -24,6 +24,7 @@ import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.test.NaiveUrlEncoder; @@ -162,12 +163,34 @@ public void testBuild() throws IOException, XMLEntityException, URISyntaxExcepti assertThat(logoutRequestStr, containsString("StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"")); LogoutResponse logoutResponse3 = new LogoutResponse(settings, httpRequest); - logoutResponse3.build("inResponseValue", Constants.STATUS_REQUEST_DENIED); + logoutResponse3.build("inResponseValue", Constants.STATUS_VERSION_MISMATCH); logoutRequestStringBase64 = logoutResponse3.getEncodedLogoutResponse(); logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); assertThat(logoutRequestStr, containsString("")); + assertThat(logoutRequestStr, not(containsString(""))); + assertThat(logoutRequestStr, not(containsString(""))); + + LogoutResponse logoutResponse4 = new LogoutResponse(settings, httpRequest); + SamlResponseStatus responseStatus = new SamlResponseStatus(Constants.STATUS_RESPONDER); + responseStatus.setSubStatusCode(Constants.STATUS_PARTIAL_LOGOUT); + logoutResponse4.build("inResponseValue", responseStatus); + logoutRequestStringBase64 = logoutResponse4.getEncodedLogoutResponse(); + logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); + assertThat(logoutRequestStr, containsString("")); + assertThat(logoutRequestStr, not(containsString(""))); + + responseStatus.setStatusMessage("status message"); + logoutResponse4.build("inResponseValue", responseStatus); + logoutRequestStringBase64 = logoutResponse4.getEncodedLogoutResponse(); + logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); + assertThat(logoutRequestStr, containsString("")); + assertThat(logoutRequestStr, containsString("status message")); } /** From 299a9b9776fbc7d0be2dfb77c6f6b88541f73f02 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 17 Aug 2021 18:58:00 +0200 Subject: [PATCH 093/133] Validate assertion version as well in SAML response validation It must be 2.0, just like the Response version. Closes #334. --- .../onelogin/saml2/authn/SamlResponse.java | 13 ++++- .../saml2/test/authn/AuthnResponseTest.java | 50 ++++++++++++++++++- .../invalid_assertion_version.xml.base64 | 1 + .../invalid_response_version.xml.base64 | 1 + 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 core/src/test/resources/data/responses/invalids/invalid_assertion_version.xml.base64 create mode 100644 core/src/test/resources/data/responses/invalids/invalid_response_version.xml.base64 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 861db7bb..ade40eea 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -191,9 +191,9 @@ public boolean isValid(String requestId) { Element rootElement = samlResponseDocument.getDocumentElement(); rootElement.normalize(); - // Check SAML version + // Check SAML version on the response if (!"2.0".equals(rootElement.getAttribute("Version"))) { - throw new ValidationError("Unsupported SAML Version.", ValidationError.UNSUPPORTED_SAML_VERSION); + throw new ValidationError("Unsupported SAML Version on Response.", ValidationError.UNSUPPORTED_SAML_VERSION); } // Check ID in the response @@ -241,6 +241,15 @@ public boolean isValid(String requestId) { + ", does not match the ID of the AuthNRequest sent by the SP: " + requestId, ValidationError.WRONG_INRESPONSETO); } + // Check SAML version on the response + NodeList assertions = queryAssertion(""); + for(int i = 0; i < assertions.getLength(); i++) { + Node versionAttribute = assertions.item(i).getAttributes().getNamedItem("Version"); + if (versionAttribute == null || !"2.0".equals(versionAttribute.getNodeValue())) { + throw new ValidationError("Unsupported SAML Version on Assertion.", ValidationError.UNSUPPORTED_SAML_VERSION); + } + } + if (!this.encrypted && settings.getWantAssertionsEncrypted()) { throw new ValidationError("The assertion of the Response is not encrypted and the SP requires it", ValidationError.NO_ENCRYPTED_ASSERTION); } 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 1b66822a..3d960ca7 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 @@ -1637,7 +1637,7 @@ public void testNoCurrentURL() throws IOException, Error, XPathExpressionExcepti /** * Tests the isValid method of SamlResponse - * Case: invalid version + * Case: invalid version (the response is not SAML 2.0) * * @throws IOException * @throws Error @@ -1655,7 +1655,53 @@ public void testValidateVersion() throws IOException, Error, XPathExpressionExce String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_saml2.xml.base64"); SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); assertFalse(samlResponse.isValid()); - assertEquals("Unsupported SAML Version.", samlResponse.getError()); + assertTrue(samlResponse.getError().startsWith("Unsupported SAML Version")); + } + + /** + * Tests the isValid method of SamlResponse + * Case: invalid response version (although the response may otherwise be valid SAML 2.0) + * + * @throws IOException + * @throws Error + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#isValid + */ + @Test + public void testValidateResponseVersion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/invalid_response_version.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse.isValid()); + assertEquals("Unsupported SAML Version on Response.", samlResponse.getError()); + } + + /** + * Tests the isValid method of SamlResponse + * Case: invalid assertion version (although the response may otherwise be valid SAML 2.0) + * + * @throws IOException + * @throws Error + * @throws ValidationError + * @throws SettingsException + * @throws SAXException + * @throws ParserConfigurationException + * @throws XPathExpressionException + * + * @see com.onelogin.saml2.authn.SamlResponse#isValid + */ + @Test + public void testValidateAssertionVersion() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + String samlResponseEncoded = Util.getFileAsString("data/responses/invalids/invalid_assertion_version.xml.base64"); + SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded)); + assertFalse(samlResponse.isValid()); + assertEquals("Unsupported SAML Version on Assertion.", samlResponse.getError()); } /** diff --git a/core/src/test/resources/data/responses/invalids/invalid_assertion_version.xml.base64 b/core/src/test/resources/data/responses/invalids/invalid_assertion_version.xml.base64 new file mode 100644 index 00000000..d310735d --- /dev/null +++ b/core/src/test/resources/data/responses/invalids/invalid_assertion_version.xml.base64 @@ -0,0 +1 @@ +CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iR09TQU1MUjEyOTAxMTc0NTcxNzk0IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiIERlc3RpbmF0aW9uPSJ7cmVjaXBpZW50fSI+CiAgPHNhbWxwOlN0YXR1cz4KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz4KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIxLjAiIElEPSJwZnhhNDY1NzRkZi1iM2IwLWEwNmEtMjNjOC02MzY0MTMxOTg3NzIiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPgogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzEzNTkwPC9zYW1sOklzc3Vlcj4KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogICAgICA8ZHM6U2lnbmVkSW5mbz4KICAgICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICAgICAgICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiI+CiAgICAgICAgICA8ZHM6VHJhbnNmb3Jtcz4KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+CiAgICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgICAgICAgIDwvZHM6VHJhbnNmb3Jtcz4KICAgICAgICAgIDxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgogICAgICAgICAgPGRzOkRpZ2VzdFZhbHVlPnBKUTdNUy9lazRLUlJXR212L0g0M1JlSFlNcz08L2RzOkRpZ2VzdFZhbHVlPgogICAgICAgIDwvZHM6UmVmZXJlbmNlPgogICAgICA8L2RzOlNpZ25lZEluZm8+CiAgICAgIDxkczpTaWduYXR1cmVWYWx1ZT55aXZlS2NQZERwdUROajZzaHJRM0FCd3IvY0EzQ3J5RDJwaEcveExac3pLV3hVNS9tbGFLdDhld2JaT2RLS3Z0T3MycEhCeTVEdWEzazk0QUYrenhHeWVsNWdPb3dtb3lYSnIrQU9yK2tQTzB2bGkxVjhvM2hQUFVad1JnU1g2UTlwUzFDcVFnaEtpRWFzUnl5bHFxSlVhUFl6bU96T0U4L1hsTWt3aVdtTzA9PC9kczpTaWduYXR1cmVWYWx1ZT4KICAgICAgPGRzOktleUluZm8+CiAgICAgICAgPGRzOlg1MDlEYXRhPgogICAgICAgICAgPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlCclRDQ0FhR2dBd0lCQWdJQkFUQURCZ0VBTUdjeEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJVd0V3WURWUVFIREF4VFlXNTBZU0JOYjI1cFkyRXhFVEFQQmdOVkJBb01DRTl1WlV4dloybHVNUmt3RndZRFZRUUREQkJoY0hBdWIyNWxiRzluYVc0dVkyOXRNQjRYRFRFd01ETXdPVEE1TlRnME5Wb1hEVEUxTURNd09UQTVOVGcwTlZvd1p6RUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZUQVRCZ05WQkFjTURGTmhiblJoSUUxdmJtbGpZVEVSTUE4R0ExVUVDZ3dJVDI1bFRHOW5hVzR4R1RBWEJnTlZCQU1NRUdGd2NDNXZibVZzYjJkcGJpNWpiMjB3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0pBb0dCQU9qU3UxZmpQeThkNXc0UXlMMSt6ZDRoSXcxTWtrZmY0V1kvVExHOE9aa1U1WVRTV21tSFBENWt2WUg1dW9YUy82cVE4MXFYcFIyd1Y4Q1Rvd1pKVUxnMDlkZFJkUm44UXNxajFGeU9DNXNsRTN5MmJaMm9GdWE3Mm9mLzQ5ZnB1am5GVDZLblE2MUNCTXFsRG9UUXFPVDYydkdKOG5QNk1aV3ZBNnN4cXVkNUFnTUJBQUV3QXdZQkFBTUJBQT09PC9kczpYNTA5Q2VydGlmaWNhdGU+CiAgICAgICAgPC9kczpYNTA5RGF0YT4KICAgICAgPC9kczpLZXlJbmZvPgogICAgPC9kczpTaWduYXR1cmU+CiAgICA8c2FtbDpTdWJqZWN0PgogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnN1cHBvcnRAb25lbG9naW4uY29tPC9zYW1sOk5hbWVJRD4KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvc2FtbDpTdWJqZWN0PgogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMTEtMThUMjE6NTI6MzdaIiBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPgogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgIDwvc2FtbDpDb25kaXRpb25zPgogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj4KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0PgogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0PgogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50PgogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmRlbW88L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4KICA8L3NhbWw6QXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPgo= \ No newline at end of file diff --git a/core/src/test/resources/data/responses/invalids/invalid_response_version.xml.base64 b/core/src/test/resources/data/responses/invalids/invalid_response_version.xml.base64 new file mode 100644 index 00000000..d8193184 --- /dev/null +++ b/core/src/test/resources/data/responses/invalids/invalid_response_version.xml.base64 @@ -0,0 +1 @@ +CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBJRD0iR09TQU1MUjEyOTAxMTc0NTcxNzk0IiBWZXJzaW9uPSIxLjAiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiIERlc3RpbmF0aW9uPSJ7cmVjaXBpZW50fSI+CiAgPHNhbWxwOlN0YXR1cz4KICAgIDxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0YXR1cz4KICA8c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElEPSJwZnhhNDY1NzRkZi1iM2IwLWEwNmEtMjNjOC02MzY0MTMxOTg3NzIiIElzc3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPgogICAgPHNhbWw6SXNzdWVyPmh0dHBzOi8vYXBwLm9uZWxvZ2luLmNvbS9zYW1sL21ldGFkYXRhLzEzNTkwPC9zYW1sOklzc3Vlcj4KICAgIDxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogICAgICA8ZHM6U2lnbmVkSW5mbz4KICAgICAgICA8ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgogICAgICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICAgICAgICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3MiI+CiAgICAgICAgICA8ZHM6VHJhbnNmb3Jtcz4KICAgICAgICAgICAgPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+CiAgICAgICAgICAgIDxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgICAgICAgIDwvZHM6VHJhbnNmb3Jtcz4KICAgICAgICAgIDxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPgogICAgICAgICAgPGRzOkRpZ2VzdFZhbHVlPnBKUTdNUy9lazRLUlJXR212L0g0M1JlSFlNcz08L2RzOkRpZ2VzdFZhbHVlPgogICAgICAgIDwvZHM6UmVmZXJlbmNlPgogICAgICA8L2RzOlNpZ25lZEluZm8+CiAgICAgIDxkczpTaWduYXR1cmVWYWx1ZT55aXZlS2NQZERwdUROajZzaHJRM0FCd3IvY0EzQ3J5RDJwaEcveExac3pLV3hVNS9tbGFLdDhld2JaT2RLS3Z0T3MycEhCeTVEdWEzazk0QUYrenhHeWVsNWdPb3dtb3lYSnIrQU9yK2tQTzB2bGkxVjhvM2hQUFVad1JnU1g2UTlwUzFDcVFnaEtpRWFzUnl5bHFxSlVhUFl6bU96T0U4L1hsTWt3aVdtTzA9PC9kczpTaWduYXR1cmVWYWx1ZT4KICAgICAgPGRzOktleUluZm8+CiAgICAgICAgPGRzOlg1MDlEYXRhPgogICAgICAgICAgPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlCclRDQ0FhR2dBd0lCQWdJQkFUQURCZ0VBTUdjeEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJVd0V3WURWUVFIREF4VFlXNTBZU0JOYjI1cFkyRXhFVEFQQmdOVkJBb01DRTl1WlV4dloybHVNUmt3RndZRFZRUUREQkJoY0hBdWIyNWxiRzluYVc0dVkyOXRNQjRYRFRFd01ETXdPVEE1TlRnME5Wb1hEVEUxTURNd09UQTVOVGcwTlZvd1p6RUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZUQVRCZ05WQkFjTURGTmhiblJoSUUxdmJtbGpZVEVSTUE4R0ExVUVDZ3dJVDI1bFRHOW5hVzR4R1RBWEJnTlZCQU1NRUdGd2NDNXZibVZzYjJkcGJpNWpiMjB3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0pBb0dCQU9qU3UxZmpQeThkNXc0UXlMMSt6ZDRoSXcxTWtrZmY0V1kvVExHOE9aa1U1WVRTV21tSFBENWt2WUg1dW9YUy82cVE4MXFYcFIyd1Y4Q1Rvd1pKVUxnMDlkZFJkUm44UXNxajFGeU9DNXNsRTN5MmJaMm9GdWE3Mm9mLzQ5ZnB1am5GVDZLblE2MUNCTXFsRG9UUXFPVDYydkdKOG5QNk1aV3ZBNnN4cXVkNUFnTUJBQUV3QXdZQkFBTUJBQT09PC9kczpYNTA5Q2VydGlmaWNhdGU+CiAgICAgICAgPC9kczpYNTA5RGF0YT4KICAgICAgPC9kczpLZXlJbmZvPgogICAgPC9kczpTaWduYXR1cmU+CiAgICA8c2FtbDpTdWJqZWN0PgogICAgICA8c2FtbDpOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnN1cHBvcnRAb25lbG9naW4uY29tPC9zYW1sOk5hbWVJRD4KICAgICAgPHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgogICAgICAgIDxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0iLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvc2FtbDpTdWJqZWN0PgogICAgPHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTAtMTEtMThUMjE6NTI6MzdaIiBOb3RPbk9yQWZ0ZXI9IjIwMTAtMTEtMThUMjI6MDI6MzdaIj4KICAgICAgPHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICA8c2FtbDpBdWRpZW5jZT57YXVkaWVuY2V9PC9zYW1sOkF1ZGllbmNlPgogICAgICA8L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgIDwvc2FtbDpDb25kaXRpb25zPgogICAgPHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDEwLTExLTE4VDIxOjU3OjM3WiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOVQyMTo1NzozN1oiIFNlc3Npb25JbmRleD0iXzUzMWMzMmQyODNiZGZmN2UwNGU0ODdiY2RiYzRkZDhkIj4KICAgICAgPHNhbWw6QXV0aG5Db250ZXh0PgogICAgICAgIDxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICA8L3NhbWw6QXV0aG5Db250ZXh0PgogICAgPC9zYW1sOkF1dGhuU3RhdGVtZW50PgogICAgPHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PgogICAgICA8c2FtbDpBdHRyaWJ1dGUgTmFtZT0idWlkIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPmRlbW88L3NhbWw6QXR0cmlidXRlVmFsdWU+CiAgICAgIDwvc2FtbDpBdHRyaWJ1dGU+CiAgICAgIDxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJhbm90aGVyX3ZhbHVlIj4KICAgICAgICA8c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhzaTp0eXBlPSJ4czpzdHJpbmciPnZhbHVlPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPgogICAgICA8L3NhbWw6QXR0cmlidXRlPgogICAgPC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD4KICA8L3NhbWw6QXNzZXJ0aW9uPgo8L3NhbWxwOlJlc3BvbnNlPgo= \ No newline at end of file From 56def43540b31fe74f8949fa8fa547cf0ce0075c Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 18 Aug 2021 11:20:17 +0200 Subject: [PATCH 094/133] Fix wrong comment --- core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ade40eea..3c785e0f 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -241,7 +241,7 @@ public boolean isValid(String requestId) { + ", does not match the ID of the AuthNRequest sent by the SP: " + requestId, ValidationError.WRONG_INRESPONSETO); } - // Check SAML version on the response + // Check SAML version on the assertion NodeList assertions = queryAssertion(""); for(int i = 0; i < assertions.getLength(); i++) { Node versionAttribute = assertions.item(i).getAttributes().getNamedItem("Version"); From 25fab2e19f194ffe590c0347dd91575d8ed24343 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 18 Aug 2021 11:33:12 +0200 Subject: [PATCH 095/133] Avoid looping over assertions (there will always be just one there) --- .../main/java/com/onelogin/saml2/authn/SamlResponse.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 3c785e0f..8fc66d8a 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -243,11 +243,9 @@ public boolean isValid(String requestId) { // Check SAML version on the assertion NodeList assertions = queryAssertion(""); - for(int i = 0; i < assertions.getLength(); i++) { - Node versionAttribute = assertions.item(i).getAttributes().getNamedItem("Version"); - if (versionAttribute == null || !"2.0".equals(versionAttribute.getNodeValue())) { - throw new ValidationError("Unsupported SAML Version on Assertion.", ValidationError.UNSUPPORTED_SAML_VERSION); - } + Node versionAttribute = assertions.item(0).getAttributes().getNamedItem("Version"); + if (versionAttribute == null || !"2.0".equals(versionAttribute.getNodeValue())) { + throw new ValidationError("Unsupported SAML Version on Assertion.", ValidationError.UNSUPPORTED_SAML_VERSION); } if (!this.encrypted && settings.getWantAssertionsEncrypted()) { From 89c2df0ecc50efe6bd96358fe4dbc33b5602f46e Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Thu, 19 Aug 2021 09:59:37 +0200 Subject: [PATCH 096/133] Allow to control NameIDPolicy.AllowCreate attribute on AuthnRequest Closes #329. --- .../onelogin/saml2/authn/AuthnRequest.java | 6 +- .../saml2/authn/AuthnRequestParams.java | 77 +++++++++++++++++-- .../saml2/test/authn/AuthnRequestTest.java | 69 +++++++++++++++++ 3 files changed, 143 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index da64ce46..b5f3811f 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -229,7 +229,11 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti if (settings.getWantNameIdEncrypted()) { nameIDPolicyFormat = Constants.NAMEID_ENCRYPTED; } - nameIDPolicyStr = ""; + String allowCreateStr = ""; + if (params.isAllowCreate()) { + allowCreateStr = " AllowCreate=\"true\""; + } + nameIDPolicyStr = ""; } valueMap.put("nameIDPolicyStr", nameIDPolicyStr); diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java index 6aec5a54..0a7efa48 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java @@ -14,9 +14,14 @@ public class AuthnRequestParams { */ private final boolean isPassive; /** - * When true the AuthNReuqest will set a nameIdPolicy + * When true the AuthNRequest will set a nameIdPolicy */ private final boolean setNameIdPolicy; + /** + * When true and {@link #setNameIdPolicy} is also true, then the + * AllowCreate='true' will be set on the NameIDPolicy element + */ + private final boolean allowCreate; /** * Indicates to the IdP the subject that should be authenticated */ @@ -29,13 +34,34 @@ public class AuthnRequestParams { * whether the ForceAuthn attribute should be set to * true * @param isPassive - * whether the isPassive attribute should be set to + * whether the IsPassive attribute should be set to * true * @param setNameIdPolicy * whether a NameIDPolicy should be set */ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy) { - this(forceAuthn, isPassive, setNameIdPolicy, null); + this(forceAuthn, isPassive, setNameIdPolicy, true); + } + + /** + * Create a set of authentication request input parameters. + * + * @param forceAuthn + * whether the ForceAuthn attribute should be set to + * true + * @param isPassive + * whether the IsPassive attribute should be set to + * true + * @param setNameIdPolicy + * whether a NameIDPolicy should be set + * @param allowCreate + * whether the AllowCreate attribute should be set to + * true on the NameIDPolicy element; only + * meaningful if setNameIdPolicy is also + * true + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, boolean allowCreate) { + this(forceAuthn, isPassive, setNameIdPolicy, allowCreate, null); } /** @@ -45,7 +71,7 @@ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setName * whether the ForceAuthn attribute should be set to * true * @param isPassive - * whether the isPassive attribute should be set to + * whether the IsPassive attribute should be set to * true * @param setNameIdPolicy * whether a NameIDPolicy should be set @@ -53,9 +79,34 @@ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setName * the subject that should be authenticated */ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, String nameIdValueReq) { + this(forceAuthn, isPassive, setNameIdPolicy, true, nameIdValueReq); + } + + /** + * Create a set of authentication request input parameters. + * + * @param forceAuthn + * whether the ForceAuthn attribute should be set to + * true + * @param isPassive + * whether the IsPassive attribute should be set to + * true + * @param setNameIdPolicy + * whether a NameIDPolicy should be set + * @param allowCreate + * the value to set for the allowCreate attribute of + * NameIDPolicy element; null means it's + * not set at all; only meaningful when + * setNameIdPolicy is true + * @param nameIdValueReq + * the subject that should be authenticated + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, boolean allowCreate, + String nameIdValueReq) { this.forceAuthn = forceAuthn; this.isPassive = isPassive; this.setNameIdPolicy = setNameIdPolicy; + this.allowCreate = allowCreate; this.nameIdValueReq = nameIdValueReq; } @@ -70,6 +121,7 @@ protected AuthnRequestParams(AuthnRequestParams source) { this.forceAuthn = source.isForceAuthn(); this.isPassive = source.isPassive(); this.setNameIdPolicy = source.isSetNameIdPolicy(); + this.allowCreate = source.isAllowCreate(); this.nameIdValueReq = source.getNameIdValueReq(); } @@ -77,25 +129,34 @@ protected AuthnRequestParams(AuthnRequestParams source) { * @return whether the ForceAuthn attribute should be set to * true */ - protected boolean isForceAuthn() { + public boolean isForceAuthn() { return forceAuthn; } /** - * @return whether the isPassive attribute should be set to + * @return whether the IsPassive attribute should be set to * true */ - protected boolean isPassive() { + public boolean isPassive() { return isPassive; } /** * @return whether a NameIDPolicy should be set */ - protected boolean isSetNameIdPolicy() { + public boolean isSetNameIdPolicy() { return setNameIdPolicy; } + /** + * @return whether the AllowCreate attribute should be set to + * true on the NameIDPolicy element (only + * meaningful if {@link #isSetNameIdPolicy()} is also true) + */ + public boolean isAllowCreate() { + return allowCreate; + } + /** * @return the subject that should be authenticated */ diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 3212fb4c..20886fb8 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -242,6 +242,75 @@ public void testNameIDPolicy() throws Exception { assertThat(authnRequestStr, containsString("Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\"")); } + /** + * Tests the AuthnRequest Constructor + * The creation of a deflated SAML Request with NameIDPolicy with and without AllowCreate + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testAllowCreate() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + // by default setNameIdPolicy=true, allowCreate=true + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString(" Date: Fri, 2 Apr 2021 19:22:12 +0200 Subject: [PATCH 097/133] Add factories as an extension mechanism for Auth This allows library consumers to extend the standard java-saml message classes with custom implementations while still using the built-in Auth class to orchestrate the message flow, by providing a mechanism to plug-in custom object creation logic. --- .../onelogin/saml2/logout/LogoutResponse.java | 6 +- .../main/java/com/onelogin/saml2/Auth.java | 133 ++++++++++- .../saml2/SamlOutgoingMessageFactory.java | 27 +++ .../saml2/SamlReceivedMessageFactory.java | 28 +++ .../com/onelogin/saml2/test/AuthTest.java | 207 ++++++++++++++++++ 5 files changed, 392 insertions(+), 9 deletions(-) create mode 100644 toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java create mode 100644 toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java 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 6deb9ee2..fcc0354d 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -95,8 +95,10 @@ public class LogoutResponse { * @param settings * OneLogin_Saml2_Settings * @param request - * the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...). - * + * the HttpRequest object to be processed (Contains GET and POST + * parameters, request URL, ...); may be null when + * building an outgoing logout response + * */ public LogoutResponse(Saml2Settings settings, HttpRequest request) { this.settings = settings; diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index dac4e4e3..e5bd0bc3 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -167,6 +167,20 @@ public class Auth { * encrypted, by default tries to return the decrypted XML */ private String lastResponse; + + private static final SamlOutgoingMessageFactory DEFAULT_AUTHN_REQUEST_FACTORY = AuthnRequest::new; + private static final SamlReceivedMessageFactory DEFAULT_SAML_RESPONSE_FACTORY = SamlResponse::new; + private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; + private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; + private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY = (settings, nothing) -> new LogoutResponse(settings, null); + private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY = LogoutResponse::new; + + private SamlOutgoingMessageFactory authnRequestFactory = DEFAULT_AUTHN_REQUEST_FACTORY; + private SamlReceivedMessageFactory samlResponseFactory = DEFAULT_SAML_RESPONSE_FACTORY; + private SamlOutgoingMessageFactory outgoingLogoutRequestFactory = DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY; + private SamlReceivedMessageFactory receivedLogoutRequestFactory = DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY; + private SamlOutgoingMessageFactory outgoingLogoutResponseFactory = DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; + private SamlReceivedMessageFactory receivedLogoutResponseFactory = DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY; /** * Initializes the SP SAML instance. @@ -609,7 +623,7 @@ public String login(String relayState, AuthnRequestParams authnRequestParams, Bo * @throws SettingsException */ public String login(String relayState, AuthnRequestParams authnRequestParams, Boolean stay, Map parameters) throws IOException, SettingsException { - AuthnRequest authnRequest = new AuthnRequest(settings, authnRequestParams); + AuthnRequest authnRequest = authnRequestFactory.create(settings, authnRequestParams); if (parameters == null) { parameters = new HashMap(); @@ -785,7 +799,7 @@ public String logout(String relayState, LogoutRequestParams logoutRequestParams, parameters = new HashMap(); } - LogoutRequest logoutRequest = new LogoutRequest(settings, logoutRequestParams); + LogoutRequest logoutRequest = outgoingLogoutRequestFactory.create(settings, logoutRequestParams); String samlLogoutRequest = logoutRequest.getEncodedLogoutRequest(); parameters.put("SAMLRequest", samlLogoutRequest); @@ -1196,7 +1210,7 @@ public void processResponse(String requestId) throws Exception { final String samlResponseParameter = httpRequest.getParameter("SAMLResponse"); if (samlResponseParameter != null) { - SamlResponse samlResponse = new SamlResponse(settings, httpRequest); + SamlResponse samlResponse = samlResponseFactory.create(settings, httpRequest); lastResponse = samlResponse.getSAMLResponseXml(); if (samlResponse.isValid(requestId)) { @@ -1229,7 +1243,7 @@ public void processResponse(String requestId) throws Exception { errors.add("invalid_response"); LOGGER.error("processResponse error. invalid_response"); LOGGER.debug(" --> " + samlResponseParameter); - } + } } } else { errors.add("invalid_binding"); @@ -1269,7 +1283,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta final String samlResponseParameter = httpRequest.getParameter("SAMLResponse"); if (samlResponseParameter != null) { - LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); + LogoutResponse logoutResponse = receivedLogoutResponseFactory.create(settings, httpRequest); lastResponse = logoutResponse.getLogoutResponseXml(); if (!logoutResponse.isValid(requestId)) { errors.add("invalid_logout_response"); @@ -1299,7 +1313,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } return null; } else if (samlRequestParameter != null) { - LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); + LogoutRequest logoutRequest = receivedLogoutRequestFactory.create(settings, httpRequest); lastRequest = logoutRequest.getLogoutRequestXml(); if (!logoutRequest.isValid()) { errors.add("invalid_logout_request"); @@ -1317,7 +1331,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } String inResponseTo = logoutRequest.id; - LogoutResponse logoutResponseBuilder = new LogoutResponse(settings, httpRequest); + LogoutResponse logoutResponseBuilder = outgoingLogoutResponseFactory.create(settings, null); logoutResponseBuilder.build(inResponseTo, Constants.STATUS_SUCCESS); lastResponse = logoutResponseBuilder.getLogoutResponseXml(); @@ -1644,4 +1658,109 @@ public String getLastRequestXML() { public String getLastResponseXML() { return lastResponse; } + + /** + * Sets the factory this {@link Auth} will use to create {@link AuthnRequest} + * objects. + *

+ * This allows consumers to provide their own extension of {@link AuthnRequest} + * possibly implementing custom features and/or XML post-processing. + * + * @param authnRequestFactory + * the factory to use to create {@link AuthnRequest} objects; if + * null, a default provider will be used which creates + * plain {@link AuthnRequest} instances + */ + public void setAuthnRequestFactory( + final SamlOutgoingMessageFactory authnRequestFactory) { + this.authnRequestFactory = authnRequestFactory != null ? authnRequestFactory + : DEFAULT_AUTHN_REQUEST_FACTORY; + } + + /** + * Sets the factory this {@link Auth} will use to create {@link SamlResponse} + * objects. + *

+ * This allows consumers to provide their own extension of {@link SamlResponse} + * possibly implementing custom features and/or XML validation. + * + * @param samlResponseFactory + * the factory to use to create {@link SamlResponse} objects; if + * null, a default factory will be used which creates + * plain {@link SamlResponse} instances + */ + public void setSamlResponseFactory(final SamlReceivedMessageFactory samlResponseFactory) { + this.samlResponseFactory = samlResponseFactory != null? samlResponseFactory: DEFAULT_SAML_RESPONSE_FACTORY; + } + + /** + * Sets the factory this {@link Auth} will use to create outgoing + * {@link LogoutRequest} objects. + *

+ * This allows consumers to provide their own extension of {@link LogoutRequest} + * possibly implementing custom features and/or XML post-processing. + * + * @param outgoingLogoutRequestFactory + * the factory to use to create outgoing {@link LogoutRequest} + * objects; if null, a default provider will be used + * which creates plain {@link LogoutRequest} instances + */ + public void setOutgoingLogoutRequestFactory(final + SamlOutgoingMessageFactory outgoingLogoutRequestFactory) { + this.outgoingLogoutRequestFactory = outgoingLogoutRequestFactory != null? outgoingLogoutRequestFactory: DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY; + } + + /** + * Sets the factory this {@link Auth} will use to create received + * {@link LogoutRequest} objects. + *

+ * This allows consumers to provide their own extension of {@link LogoutRequest} + * possibly implementing custom features and/or XML validation. + * + * @param receivedLogoutRequestFactory + * the factory to use to create received {@link LogoutRequest} + * objects; if null, a default provider will be used + * which creates plain {@link LogoutRequest} instances + */ + public void setReceivedLogoutRequestFactory( + final SamlReceivedMessageFactory receivedLogoutRequestFactory) { + this.receivedLogoutRequestFactory = receivedLogoutRequestFactory != null ? receivedLogoutRequestFactory + : DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY; + } + + /** + * Sets the factory this {@link Auth} will use to create outgoing + * {@link LogoutResponse} objects. + *

+ * This allows consumers to provide their own extension of + * {@link LogoutResponse} possibly implementing custom features and/or XML + * post-processing. + * + * @param outgoingLogoutResponseFactory + * the factory to use to create outgoing {@link LogoutResponse} + * objects; if null, a default provider will be used + * which creates plain {@link LogoutResponse} instances + */ + public void setOutgoingLogoutResponseFactory(final + SamlOutgoingMessageFactory outgoingLogoutResponseFactory) { + this.outgoingLogoutResponseFactory = outgoingLogoutResponseFactory != null? outgoingLogoutResponseFactory: DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; + } + + /** + * Sets the factory this {@link Auth} will use to create received + * {@link LogoutResponse} objects. + *

+ * This allows consumers to provide their own extension of + * {@link LogoutResponse} possibly implementing custom features and/or XML + * validation. + * + * @param receivedLogoutResponseFactory + * the factory to use to create received {@link LogoutResponse} + * objects; if null, a default provider will be used + * which creates plain {@link LogoutResponse} instances + */ + public void setReceivedLogoutResponseFactory(final + SamlReceivedMessageFactory receivedLogoutResponseFactory) { + this.receivedLogoutResponseFactory = receivedLogoutResponseFactory != null? receivedLogoutResponseFactory: DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY; + } } diff --git a/toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java new file mode 100644 index 00000000..8c83b10f --- /dev/null +++ b/toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java @@ -0,0 +1,27 @@ +package com.onelogin.saml2; + +import com.onelogin.saml2.settings.Saml2Settings; + +/** + * Factory which can create an outgoing SAML message object from a + * {@link Saml2Settings} instance and other input parameters. + * + * @param + * the type of input parameters required + * @param + * the type of SAML outgoing message object created + */ +@FunctionalInterface +public interface SamlOutgoingMessageFactory { + + /** + * Creates an outgoing SAML message object. + * + * @param settings + * the settings + * @param params + * the input parameters + * @return the created received SAML message object + */ + R create(Saml2Settings settings, U params); +} \ No newline at end of file diff --git a/toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java new file mode 100644 index 00000000..c086c558 --- /dev/null +++ b/toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java @@ -0,0 +1,28 @@ +package com.onelogin.saml2; + +import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.settings.Saml2Settings; + +/** + * Factory which can create a received SAML message object from a + * {@link Saml2Settings} instance and other input parameters. + * + * @param + * the type of received SAML message object created + */ +@FunctionalInterface +public interface SamlReceivedMessageFactory { + + /** + * Creates a received SAML message object. + * + * @param settings + * the settings + * @param httpRequest + * the HTTP request + * @return the created received SAML message object + * @throws Exception + * if the message creation fails + */ + R create(Saml2Settings settings, HttpRequest httpRequest) throws Exception; +} \ No newline at end of file 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 ff8f1e54..8679033e 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -10,6 +10,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.matches; @@ -47,13 +48,19 @@ import org.w3c.dom.Document; import com.onelogin.saml2.Auth; +import com.onelogin.saml2.authn.AuthnRequest; import com.onelogin.saml2.authn.AuthnRequestParams; +import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.XMLEntityException; +import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutRequestParams; +import com.onelogin.saml2.logout.LogoutResponse; import com.onelogin.saml2.model.KeyStoreSettings; +import com.onelogin.saml2.servlet.ServletUtils; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Constants; @@ -2221,4 +2228,204 @@ public void testGetLastLogoutResponseReceived() throws Exception { String logoutResponseXML = auth.getLastResponseXML(); assertThat(logoutResponseXML, containsString(" new LogoutResponseEx(settings, null)); + auth.processSLO(false, null); + } + + /** + * Tests that the received LogoutResponse factory gets invoked by Auth and the right parameters are passed to it. + * + * @throws Exception + * + * @see com.onelogin.saml2.Auth#setReceivedLogoutResponseFactory(com.onelogin.saml2.SamlReceivedMessageFactory) + */ + @Test(expected = FactoryInvokedException.class) + public void testReceivedLogoutResponseFactory() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + when(request.getRequestURL()).thenReturn(new StringBuffer("http://stuff.com/endpoints/endpoints/sls.php")); + when(request.getSession()).thenReturn(session); + + String samlResponseEncoded = Util.getFileAsString("data/logout_responses/logout_response_deflated.xml.base64"); + when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded})); + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + class LogoutResponseEx extends LogoutResponse { + + public LogoutResponseEx(Saml2Settings sett, HttpRequest req) { + super(sett, req); + assertSame(settings, sett); + assertEquals(ServletUtils.makeHttpRequest(request), req); + throw new FactoryInvokedException(); + } + + } + + Auth auth = new Auth(settings, request, response); + auth.setReceivedLogoutResponseFactory(LogoutResponseEx::new); + auth.processSLO(); + } } From 1246cd5e359fb193c9edaa4e05a33103eaf61ca5 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 27 Jul 2021 14:35:21 +0200 Subject: [PATCH 098/133] Change syntax to avoid javac 8 compilation errors It seems like neither Eclipse ecj nor javac in Java 11 complains when using constructor references in these very special cases used for unit testing. But javac in Java 8 does. So, we're now using lambda expressions in place of constructor references: this seems to make all compilers happy. --- .../test/java/com/onelogin/saml2/test/AuthTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 8679033e..a77e5583 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -2257,7 +2257,7 @@ public AuthnRequestEx(Saml2Settings sett, AuthnRequestParams par) { } Auth auth = new Auth(settings, request, response); - auth.setAuthnRequestFactory(AuthnRequestEx::new); + auth.setAuthnRequestFactory((sett, param) -> new AuthnRequestEx(sett, param)); auth.login(params); } @@ -2289,7 +2289,7 @@ public SamlResponseEx(Saml2Settings sett, HttpRequest req) throws Exception { } Auth auth = new Auth(settings, request, response); - auth.setSamlResponseFactory(SamlResponseEx::new); + auth.setSamlResponseFactory((sett, req) -> new SamlResponseEx(sett, req)); auth.processResponse(); } @@ -2320,7 +2320,7 @@ public LogoutRequestEx(Saml2Settings sett, LogoutRequestParams par) { } Auth auth = new Auth(settings, request, response); - auth.setOutgoingLogoutRequestFactory(LogoutRequestEx::new); + auth.setOutgoingLogoutRequestFactory((sett, param) -> new LogoutRequestEx(sett, param)); auth.logout(null, params); } @@ -2355,7 +2355,7 @@ public LogoutRequestEx(Saml2Settings sett, HttpRequest req) { } Auth auth = new Auth(settings, request, response); - auth.setReceivedLogoutRequestFactory(LogoutRequestEx::new); + auth.setReceivedLogoutRequestFactory((sett, req) -> new LogoutRequestEx(sett, req)); auth.processSLO(); } @@ -2425,7 +2425,7 @@ public LogoutResponseEx(Saml2Settings sett, HttpRequest req) { } Auth auth = new Auth(settings, request, response); - auth.setReceivedLogoutResponseFactory(LogoutResponseEx::new); + auth.setReceivedLogoutResponseFactory((sett, req) -> new LogoutResponseEx(sett, req)); auth.processSLO(); } } From abd632656b151bd8b984e2a5f055fccc314d63f5 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 13 Aug 2021 19:13:04 +0200 Subject: [PATCH 099/133] Move message factories to their own package --- toolkit/src/main/java/com/onelogin/saml2/Auth.java | 2 ++ .../{ => factory}/SamlOutgoingMessageFactory.java | 2 +- .../{ => factory}/SamlReceivedMessageFactory.java | 2 +- .../test/java/com/onelogin/saml2/test/AuthTest.java | 12 ++++++------ 4 files changed, 10 insertions(+), 8 deletions(-) rename toolkit/src/main/java/com/onelogin/saml2/{ => factory}/SamlOutgoingMessageFactory.java (94%) rename toolkit/src/main/java/com/onelogin/saml2/{ => factory}/SamlReceivedMessageFactory.java (95%) diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index e5bd0bc3..d3c579bb 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -26,6 +26,8 @@ import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.factory.SamlOutgoingMessageFactory; +import com.onelogin.saml2.factory.SamlReceivedMessageFactory; import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutRequest; diff --git a/toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java similarity index 94% rename from toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java rename to toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java index 8c83b10f..7cbdd32c 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/SamlOutgoingMessageFactory.java +++ b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java @@ -1,4 +1,4 @@ -package com.onelogin.saml2; +package com.onelogin.saml2.factory; import com.onelogin.saml2.settings.Saml2Settings; diff --git a/toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java similarity index 95% rename from toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java rename to toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java index c086c558..0a78a800 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/SamlReceivedMessageFactory.java +++ b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java @@ -1,4 +1,4 @@ -package com.onelogin.saml2; +package com.onelogin.saml2.factory; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.settings.Saml2Settings; 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 a77e5583..6bf588f1 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -2237,7 +2237,7 @@ private static class FactoryInvokedException extends RuntimeException { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setAuthnRequestFactory(com.onelogin.saml2.SamlOutgoingMessageFactory) + * @see com.onelogin.saml2.Auth#setAuthnRequestFactory(com.onelogin.saml2.factory.SamlOutgoingMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testAuthnRequestFactory() throws Exception { @@ -2266,7 +2266,7 @@ public AuthnRequestEx(Saml2Settings sett, AuthnRequestParams par) { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setSamlResponseFactory(com.onelogin.saml2.SamlReceivedMessageFactory) + * @see com.onelogin.saml2.Auth#setSamlResponseFactory(com.onelogin.saml2.factory.SamlReceivedMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testSamlResponseFactory() throws Exception { @@ -2298,7 +2298,7 @@ public SamlResponseEx(Saml2Settings sett, HttpRequest req) throws Exception { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setOutgoingLogoutRequestFactory(com.onelogin.saml2.SamlOutgoingMessageFactory) + * @see com.onelogin.saml2.Auth#setOutgoingLogoutRequestFactory(com.onelogin.saml2.factory.SamlOutgoingMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testOutgoingLogoutRequestFactory() throws Exception { @@ -2329,7 +2329,7 @@ public LogoutRequestEx(Saml2Settings sett, LogoutRequestParams par) { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setReceivedLogoutRequestFactory(com.onelogin.saml2.SamlReceivedMessageFactory) + * @see com.onelogin.saml2.Auth#setReceivedLogoutRequestFactory(com.onelogin.saml2.factory.SamlReceivedMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testReceivedLogoutRequestFactory() throws Exception { @@ -2364,7 +2364,7 @@ public LogoutRequestEx(Saml2Settings sett, HttpRequest req) { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setOutgoingLogoutResponseFactory(com.onelogin.saml2.SamlOutgoingMessageFactory) + * @see com.onelogin.saml2.Auth#setOutgoingLogoutResponseFactory(com.onelogin.saml2.factory.SamlOutgoingMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testOutgoingLogoutResponseFactory() throws Exception { @@ -2399,7 +2399,7 @@ public LogoutResponseEx(Saml2Settings sett, HttpRequest req) { * * @throws Exception * - * @see com.onelogin.saml2.Auth#setReceivedLogoutResponseFactory(com.onelogin.saml2.SamlReceivedMessageFactory) + * @see com.onelogin.saml2.Auth#setReceivedLogoutResponseFactory(com.onelogin.saml2.factory.SamlReceivedMessageFactory) */ @Test(expected = FactoryInvokedException.class) public void testReceivedLogoutResponseFactory() throws Exception { From 11bb45d30145430c653b7d0a46ab8e586852a810 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 13 Aug 2021 21:54:51 +0200 Subject: [PATCH 100/133] Improve LogoutResponse API to match LogoutRequest/AuthnRequest ones This introduces LogoutResponseParams and allows to make the whole API coherent when building outgoing SAML messages. The Auth factories benefit from this as well, because they now share a common construction and usage pattern. --- .../onelogin/saml2/logout/LogoutResponse.java | 136 +++++++--- .../saml2/logout/LogoutResponseParams.java | 105 ++++++++ .../saml2/test/logout/LogoutResponseTest.java | 243 +++++++++++++++--- .../main/java/com/onelogin/saml2/Auth.java | 11 +- .../com/onelogin/saml2/test/AuthTest.java | 14 +- 5 files changed, 425 insertions(+), 84 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/logout/LogoutResponseParams.java 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 fcc0354d..7199fae9 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -69,11 +69,6 @@ public class LogoutResponse { */ private String currentUrl; - /** - * The inResponseTo attribute of the Logout Request - */ - private String inResponseTo; - /** * Time when the Logout Request was created */ @@ -85,12 +80,8 @@ public class LogoutResponse { private Exception validationException; /** - * The respone status code and messages - */ - private SamlResponseStatus responseStatus; - - /** - * Constructs the LogoutResponse object. + * Constructs the LogoutResponse object when a received response should be + * extracted from the HTTP request and parsed. * * @param settings * OneLogin_Saml2_Settings @@ -115,6 +106,25 @@ public LogoutResponse(Saml2Settings settings, HttpRequest request) { logoutResponseDocument = Util.loadXML(logoutResponseString); } } + + /** + * Constructs the LogoutResponse object when a new response should be generated + * and sent. + * + * @param settings + * OneLogin_Saml2_Settings + * @param params + * a set of logout response input parameters that shape the + * request to create + */ + public LogoutResponse(Saml2Settings settings, LogoutResponseParams params) { + this.settings = settings; + this.request = null; + id = Util.generateUniqueID(settings.getUniqueIDPrefix()); + issueInstant = Calendar.getInstance(); + StrSubstitutor substitutor = generateSubstitutor(params, settings); + this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate()), params, settings); + } /** * @return the base64 encoded unsigned Logout Response (deflated or not) @@ -355,21 +365,33 @@ protected NodeList query (String query) throws XPathExpressionException { return Util.query(this.logoutResponseDocument, query, null); } - /** - * Generates a Logout Response XML string. - * - * @param inResponseTo - * InResponseTo attribute value to bet set at the Logout Response. + /** + * Generates a Logout Response XML string. + * + * @param inResponseTo + * InResponseTo attribute value to bet set at the Logout Response. * @param responseStatus - * SamlResponseStatus response status to be set on the LogoutResponse - */ + * SamlResponseStatus response status to be set on the + * LogoutResponse + * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} + * instead, in which case this method becomes completely useless; + * indeed, invoking this method in an outgoing logout response + * scenario will cause the response parameters specified at + * construction time (inResponseTo and + * responseStatus) to be overwritten, as well as its + * generated id and issue instant; on the other hand invoking this + * method in a received logout response scenario does not make sense + * at all (and in that case + * {@link #LogoutResponse(Saml2Settings, HttpRequest)} should be + * used instead) + */ + @Deprecated public void build(String inResponseTo, SamlResponseStatus responseStatus) { id = Util.generateUniqueID(settings.getUniqueIDPrefix()); issueInstant = Calendar.getInstance(); - this.inResponseTo = inResponseTo; - - StrSubstitutor substitutor = generateSubstitutor(settings, responseStatus); - this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate()), settings); + final LogoutResponseParams params = new LogoutResponseParams(inResponseTo, responseStatus); + StrSubstitutor substitutor = generateSubstitutor(params, settings); + this.logoutResponseString = postProcessXml(substitutor.replace(getLogoutResponseTemplate()), params, settings); } /** @@ -379,24 +401,61 @@ public void build(String inResponseTo, SamlResponseStatus responseStatus) { * InResponseTo attribute value to bet set at the Logout Response. * @param statusCode * String StatusCode to be set on the LogoutResponse + * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} + * instead, in which case this method becomes completely useless; + * indeed, invoking this method in an outgoing logout response + * scenario will cause the response parameters specified at + * construction time (inResponseTo and + * responseStatus) to be overwritten, as well as its + * generated id and issue instant; on the other hand invoking this + * method in a received logout response scenario does not make sense + * at all (and in that case + * {@link #LogoutResponse(Saml2Settings, HttpRequest)} should be + * used instead) */ + @Deprecated public void build(String inResponseTo, String statusCode) { build(inResponseTo, new SamlResponseStatus(statusCode)); } - /** - * Generates a Logout Response XML string. - * - * @param inResponseTo - * InResponseTo attribute value to bet set at the Logout Response. - */ + /** + * Generates a Logout Response XML string. + * + * @param inResponseTo + * InResponseTo attribute value to bet set at the Logout Response. + * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} + * instead, in which case this method becomes completely useless; + * indeed, invoking this method in an outgoing logout response + * scenario will cause the response parameters specified at + * construction time (inResponseTo and + * responseStatus) to be overwritten, as well as its + * generated id and issue instant; on the other hand invoking this + * method in a received logout response scenario does not make sense + * at all (and in that case + * {@link #LogoutResponse(Saml2Settings, HttpRequest)} should be + * used instead) + */ + @Deprecated public void build(String inResponseTo) { build(inResponseTo, Constants.STATUS_SUCCESS); } - /** - * Generates a Logout Response XML string. - */ + /** + * Generates a Logout Response XML string. + * + * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} + * instead, in which case this method becomes completely useless; + * indeed, invoking this method in an outgoing logout response + * scenario will cause the response parameters specified at + * construction time (inResponseTo and + * responseStatus) to be overwritten, as well as its + * generated id and issue instant; on the other hand invoking this + * method in a received logout response scenario does not make sense + * at all (and in that case + * {@link #LogoutResponse(Saml2Settings, HttpRequest)} should be + * used instead) + */ + @Deprecated public void build() { build(null); } @@ -412,26 +471,29 @@ public void build() { * @param logoutResponseXml * the XML produced for this LogoutResponse by the standard * implementation provided by {@link LogoutResponse} + * @param params + * the logout request input parameters * @param settings * the settings * @return the post-processed XML for this LogoutResponse, which will then be * returned by any call to {@link #getLogoutResponseXml()} */ - protected String postProcessXml(final String logoutResponseXml, final Saml2Settings settings) { + protected String postProcessXml(final String logoutResponseXml, final LogoutResponseParams params, + final Saml2Settings settings) { return logoutResponseXml; } /** * Substitutes LogoutResponse variables within a string by values. * + * @param params + * the logout response input parameters * @param settings - * Saml2Settings object. Setting data - * @param responseStatus - * SamlResponseStatus response status to be set on the LogoutResponse + * Saml2Settings object. Setting data * * @return the StrSubstitutor object of the LogoutResponse */ - private StrSubstitutor generateSubstitutor(Saml2Settings settings, SamlResponseStatus responseStatus) { + private StrSubstitutor generateSubstitutor(LogoutResponseParams params, Saml2Settings settings) { Map valueMap = new HashMap(); valueMap.put("id", Util.toXml(id)); @@ -447,12 +509,14 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings, SamlResponseS valueMap.put("destinationStr", destinationStr); String inResponseStr = ""; + final String inResponseTo = params.getInResponseTo(); if (inResponseTo != null) { inResponseStr = " InResponseTo=\"" + Util.toXml(inResponseTo) + "\""; } valueMap.put("inResponseStr", inResponseStr); StringBuilder statusStr = new StringBuilder("inResponseTo attribute and a + * response status with a top-level {@link Constants#STATUS_SUCCESS} status + * code. + */ + public LogoutResponseParams() { + this((String) null); + } + + /** + * Creates a logout response with a response status with a top-level + * {@link Constants#STATUS_SUCCESS} status code. + * + * @param inResponseTo + * the id of the logout request the response refers to; may be + * null if such id cannot be determined (possibly + * because the request is malformed) + */ + public LogoutResponseParams(String inResponseTo) { + this(inResponseTo, Constants.STATUS_SUCCESS); + } + + /** + * Creates a logout response. + * + * @param inResponseTo + * the id of the logout request the response refers to; may be + * null if such id cannot be determined (possibly + * because the request is malformed) + * @param statusCode + * the top-level status code code to set on the response + */ + public LogoutResponseParams(String inResponseTo, String statusCode) { + this(inResponseTo, new SamlResponseStatus(statusCode)); + } + + /** + * Creates a logout response. + * + * @param inResponseTo + * the id of the logout request the response refers to; may be + * null if such id cannot be determined (possibly + * because the request is malformed) + * @param responseStatus + * the response status; should not be null + * @throws NullPointerException + * if the specified response status is null + */ + public LogoutResponseParams(String inResponseTo, SamlResponseStatus responseStatus) throws NullPointerException { + this.inResponseTo = inResponseTo; + this.responseStatus = responseStatus; + if (responseStatus == null) + throw new NullPointerException("response status must not be null"); + } + + /** + * Create a set of logout request input parameters, by copying them from another + * set. + * + * @param source + * the source set of logout request input parameters + */ + protected LogoutResponseParams(LogoutResponseParams source) { + this.inResponseTo = source.getInResponseTo(); + this.responseStatus = source.getResponseStatus(); + } + + /** + * Returns the response status. + * + * @return the response status + */ + public SamlResponseStatus getResponseStatus() { + return responseStatus; + } + + /** + * Returns the id of the logout request this response refers to. + * + * @return the inResponseTo + */ + public String getInResponseTo() { + return inResponseTo; + } +} \ No newline at end of file diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index c561aec3..dbc68c51 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -24,6 +24,7 @@ import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.logout.LogoutResponseParams; import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -114,7 +115,7 @@ public String getLogoutResponseXml() { * @see com.onelogin.saml2.logout.LogoutResponse */ @Test - public void testConstructor() throws IOException, XMLEntityException, URISyntaxException, Error { + public void testReceivedMessageConstructor() throws IOException, XMLEntityException, URISyntaxException, Error { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); String samlResponseEncoded = Util.getFileAsString("data/logout_responses/logout_response_deflated.xml.base64"); final String requestURL = "/"; @@ -125,7 +126,7 @@ public void testConstructor() throws IOException, XMLEntityException, URISyntaxE String logoutResponseStringBase64 = logoutResponse.getEncodedLogoutResponse(); assertEquals(logoutResponseStringBase64, expectedLogoutResponseStringBase64); } - + /** * Tests the build method of LogoutResponse * @@ -137,6 +138,66 @@ public void testConstructor() throws IOException, XMLEntityException, URISyntaxE * @see com.onelogin.saml2.logout.LogoutResponse#build */ @Test + public void testOutgoingMessageConstructor() throws IOException, XMLEntityException, URISyntaxException, Error { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + LogoutResponse logoutResponse = new LogoutResponse(settings, new LogoutResponseParams()); + assertFalse(logoutResponse.isValid()); + assertEquals("SAML Logout Response is not loaded", logoutResponse.getError()); + String logoutResponseStringBase64 = logoutResponse.getEncodedLogoutResponse(); + assertFalse(logoutResponseStringBase64.isEmpty()); + + String logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, not(containsString(""))); + assertThat(logoutResponseStr, not(containsString(""))); + + SamlResponseStatus responseStatus = new SamlResponseStatus(Constants.STATUS_RESPONDER); + responseStatus.setSubStatusCode(Constants.STATUS_PARTIAL_LOGOUT); + LogoutResponse logoutResponse4 = new LogoutResponse(settings, new LogoutResponseParams("inResponseValue", responseStatus)); + logoutResponseStringBase64 = logoutResponse4.getEncodedLogoutResponse(); + logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, not(containsString(""))); + + responseStatus.setStatusMessage("status message"); + LogoutResponse logoutResponse5 = new LogoutResponse(settings, new LogoutResponseParams("inResponseValue", responseStatus)); + logoutResponseStringBase64 = logoutResponse5.getEncodedLogoutResponse(); + logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, containsString("status message")); + } + + /** + * Tests the legacy build method of LogoutResponse + * + * @throws IOException + * @throws XMLEntityException + * @throws URISyntaxException + * @throws Error + * + * @see com.onelogin.saml2.logout.LogoutResponse#build + */ + @Test public void testBuild() throws IOException, XMLEntityException, URISyntaxException, Error { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); @@ -147,50 +208,50 @@ public void testBuild() throws IOException, XMLEntityException, URISyntaxExcepti assertFalse(logoutResponse.isValid()); assertEquals("SAML Logout Response is not loaded", logoutResponse.getError()); logoutResponse.build(); - String logoutRequestStringBase64 = logoutResponse.getEncodedLogoutResponse(); - assertFalse(logoutRequestStringBase64.isEmpty()); + String logoutResponseStringBase64 = logoutResponse.getEncodedLogoutResponse(); + assertFalse(logoutResponseStringBase64.isEmpty()); - String logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); - assertThat(logoutRequestStr, containsString("")); - assertThat(logoutRequestStr, not(containsString(""))); - assertThat(logoutRequestStr, not(containsString(""))); + logoutResponseStringBase64 = logoutResponse3.getEncodedLogoutResponse(); + logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, not(containsString(""))); + assertThat(logoutResponseStr, not(containsString(""))); LogoutResponse logoutResponse4 = new LogoutResponse(settings, httpRequest); SamlResponseStatus responseStatus = new SamlResponseStatus(Constants.STATUS_RESPONDER); responseStatus.setSubStatusCode(Constants.STATUS_PARTIAL_LOGOUT); logoutResponse4.build("inResponseValue", responseStatus); - logoutRequestStringBase64 = logoutResponse4.getEncodedLogoutResponse(); - logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); - assertThat(logoutRequestStr, containsString("")); - assertThat(logoutRequestStr, not(containsString(""))); + logoutResponseStringBase64 = logoutResponse4.getEncodedLogoutResponse(); + logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, not(containsString(""))); responseStatus.setStatusMessage("status message"); logoutResponse4.build("inResponseValue", responseStatus); - logoutRequestStringBase64 = logoutResponse4.getEncodedLogoutResponse(); - logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64); - assertThat(logoutRequestStr, containsString("")); - assertThat(logoutRequestStr, containsString("status message")); + logoutResponseStringBase64 = logoutResponse4.getEncodedLogoutResponse(); + logoutResponseStr = Util.base64decodedInflated(logoutResponseStringBase64); + assertThat(logoutResponseStr, containsString("")); + assertThat(logoutResponseStr, containsString("status message")); } /** @@ -203,7 +264,32 @@ public void testBuild() throws IOException, XMLEntityException, URISyntaxExcepti @Test public void testGetLogoutResponseXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); - LogoutResponse logoutResponse = new LogoutResponse(settings, null); + LogoutResponse logoutResponse = new LogoutResponse(settings, new LogoutResponseParams()); + String logoutResponseXML = logoutResponse.getLogoutResponseXml(); + assertThat(logoutResponseXML, containsString(" + * Case: logout destination contains special chars and the legacy build method is used to build the outgoing response. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutResponse#getLogoutResponseXml + */ + @Test + public void testGetLogoutResponseXmlSpecialCharsLegacy() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_specialchars.properties").build(); + LogoutResponse logoutResponse = new LogoutResponse(settings, (HttpRequest) null); logoutResponse.build(); String logoutResponseXML = logoutResponse.getLogoutResponseXml(); assertThat(logoutResponseXML, containsString(" - * Case: LogoutResponse message built by the caller. + * Case: outgoing LogoutResponse message created by the caller. + * + * @throws IOException + * @throws Error + * @throws ValidationError + * + * @see com.onelogin.saml2.logout.LogoutResponse#getIssueInstant() + */ + @Test + public void testGetIssueInstantOutgoingMessage() throws IOException, Error, ValidationError { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + long start = System.currentTimeMillis(); + LogoutResponse logoutResponse = new LogoutResponse(settings, new LogoutResponseParams()); + long end = System.currentTimeMillis(); + Calendar issueInstant = logoutResponse.getIssueInstant(); + assertNotNull(issueInstant); + long millis = issueInstant.getTimeInMillis(); + assertTrue(millis >= start && millis <= end); + } + + /** + * Tests the getIssueInstant method of LogoutResponse + *

+ * Case: outgoing LogoutResponse message created by the caller and legacy build() method invoked * * @throws IOException * @throws Error @@ -330,10 +465,10 @@ public void testGetIssueInstant() throws IOException, Error, ValidationError { * @see com.onelogin.saml2.logout.LogoutResponse#getIssueInstant() */ @Test - public void testGetIssueInstantBuiltMessage() throws IOException, Error, ValidationError { + public void testGetIssueInstantOutgoingMessageLegacy() throws IOException, Error, ValidationError { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + LogoutResponse logoutResponse = new LogoutResponse(settings, new LogoutResponseParams()); long start = System.currentTimeMillis(); - LogoutResponse logoutResponse = new LogoutResponse(settings, null); logoutResponse.build(); long end = System.currentTimeMillis(); Calendar issueInstant = logoutResponse.getIssueInstant(); @@ -779,11 +914,41 @@ private static HttpRequest newHttpRequest(String requestURL, String samlResponse @Test public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); - LogoutResponse logoutResponse = new LogoutResponse(settings, null) { + final LogoutResponseParams params = new LogoutResponseParams(); + LogoutResponse logoutResponse = new LogoutResponse(settings, params) { + @Override + protected String postProcessXml(String logoutResponseXml, LogoutResponseParams par, Saml2Settings sett) { + assertEquals(logoutResponseXml, super.postProcessXml(logoutResponseXml, par, sett)); + assertSame(settings, sett); + assertSame(params, par); + return "changed"; + } + }; + assertEquals("changed", logoutResponse.getLogoutResponseXml()); + } + + /** + * Tests the postProcessXml method of LogoutResponse + * + * Case: the legacy build method is used to build the outgoing response. + * + * @throws Exception + * + * @see com.onelogin.saml2.logout.LogoutResponse#postProcessXml + */ + @Test + public void testPostProcessXmlLegacy() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + LogoutResponse logoutResponse = new LogoutResponse(settings, (HttpRequest) null) { @Override - protected String postProcessXml(String logoutResponseXml, Saml2Settings sett) { - assertEquals(logoutResponseXml, super.postProcessXml(logoutResponseXml, sett)); + protected String postProcessXml(String logoutResponseXml, LogoutResponseParams params, Saml2Settings sett) { + assertEquals(logoutResponseXml, super.postProcessXml(logoutResponseXml, params, sett)); assertSame(settings, sett); + assertNull(params.getInResponseTo()); + SamlResponseStatus responseStatus = params.getResponseStatus(); + assertEquals(Constants.STATUS_SUCCESS, responseStatus.getStatusCode()); + assertNull(responseStatus.getSubStatusCode()); + assertNull(responseStatus.getStatusMessage()); return "changed"; } }; diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index d3c579bb..e0bb66c3 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -33,6 +33,7 @@ import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutRequestParams; import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.logout.LogoutResponseParams; import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.servlet.ServletUtils; @@ -174,14 +175,14 @@ public class Auth { private static final SamlReceivedMessageFactory DEFAULT_SAML_RESPONSE_FACTORY = SamlResponse::new; private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; - private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY = (settings, nothing) -> new LogoutResponse(settings, null); + private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY = LogoutResponse::new; private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY = LogoutResponse::new; private SamlOutgoingMessageFactory authnRequestFactory = DEFAULT_AUTHN_REQUEST_FACTORY; private SamlReceivedMessageFactory samlResponseFactory = DEFAULT_SAML_RESPONSE_FACTORY; private SamlOutgoingMessageFactory outgoingLogoutRequestFactory = DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY; private SamlReceivedMessageFactory receivedLogoutRequestFactory = DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY; - private SamlOutgoingMessageFactory outgoingLogoutResponseFactory = DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; + private SamlOutgoingMessageFactory outgoingLogoutResponseFactory = DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; private SamlReceivedMessageFactory receivedLogoutResponseFactory = DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY; /** @@ -1333,8 +1334,8 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } String inResponseTo = logoutRequest.id; - LogoutResponse logoutResponseBuilder = outgoingLogoutResponseFactory.create(settings, null); - logoutResponseBuilder.build(inResponseTo, Constants.STATUS_SUCCESS); + LogoutResponse logoutResponseBuilder = outgoingLogoutResponseFactory.create(settings, + new LogoutResponseParams(inResponseTo, Constants.STATUS_SUCCESS)); lastResponse = logoutResponseBuilder.getLogoutResponseXml(); String samlLogoutResponse = logoutResponseBuilder.getEncodedLogoutResponse(); @@ -1744,7 +1745,7 @@ public void setReceivedLogoutRequestFactory( * which creates plain {@link LogoutResponse} instances */ public void setOutgoingLogoutResponseFactory(final - SamlOutgoingMessageFactory outgoingLogoutResponseFactory) { + SamlOutgoingMessageFactory outgoingLogoutResponseFactory) { this.outgoingLogoutResponseFactory = outgoingLogoutResponseFactory != null? outgoingLogoutResponseFactory: DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; } 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 6bf588f1..6846bfa7 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -59,7 +59,9 @@ import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutRequestParams; import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.logout.LogoutResponseParams; import com.onelogin.saml2.model.KeyStoreSettings; +import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.servlet.ServletUtils; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; @@ -2380,17 +2382,21 @@ public void testOutgoingLogoutResponseFactory() throws Exception { class LogoutResponseEx extends LogoutResponse { - public LogoutResponseEx(Saml2Settings sett, HttpRequest req) { - super(sett, req); + public LogoutResponseEx(Saml2Settings sett, LogoutResponseParams par) { + super(sett, par); assertSame(settings, sett); - assertNull(req); + assertEquals("ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e", par.getInResponseTo()); + SamlResponseStatus responseStatus = par.getResponseStatus(); + assertEquals(Constants.STATUS_SUCCESS, responseStatus.getStatusCode()); + assertNull(responseStatus.getSubStatusCode()); + assertNull(responseStatus.getStatusMessage()); throw new FactoryInvokedException(); } } Auth auth = new Auth(settings, request, response); - auth.setOutgoingLogoutResponseFactory((sett, nothing) -> new LogoutResponseEx(settings, null)); + auth.setOutgoingLogoutResponseFactory((sett, param) -> new LogoutResponseEx(settings, param)); auth.processSLO(false, null); } From 41d95ac8c310a1a05c1f666446f55f20ff763b82 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 13 Aug 2021 21:58:29 +0200 Subject: [PATCH 101/133] Apply minor adjustments to AuthnRequest/LogoutRequest APIs and tests These details were overlooked in the first place: getters of the input params should better be public and fields can be declared as final. The useless NameId setter in LogoutRequestParams was temporarily introduced during development but should have been reverted from the beginning, so it's gone now. Some tests were improved to provide more accurate assertions. --- .../saml2/authn/AuthnRequestParams.java | 2 +- .../onelogin/saml2/logout/LogoutRequest.java | 2 +- .../saml2/logout/LogoutRequestParams.java | 31 +++++++------------ .../saml2/test/logout/LogoutRequestTest.java | 8 +++-- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java index 0a7efa48..69954254 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java @@ -160,7 +160,7 @@ public boolean isAllowCreate() { /** * @return the subject that should be authenticated */ - protected String getNameIdValueReq() { + public String getNameIdValueReq() { return nameIdValueReq; } } \ No newline at end of file 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 234b89ac..cacd7bf8 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -242,7 +242,7 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request) { * @param settings * OneLogin_Saml2_Settings * @param params - * a set of authentication request input parameters that shape the + * a set of logout request input parameters that shape the * request to create */ public LogoutRequest(Saml2Settings settings, LogoutRequestParams params) { diff --git a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java index ce95626f..79cd2ee8 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequestParams.java @@ -9,30 +9,31 @@ public class LogoutRequestParams { * SessionIndex. When the user is logged, this stored it from the AuthnStatement * of the SAML Response */ - private String sessionIndex; + private final String sessionIndex; /** * NameID. */ - private String nameId; + private final String nameId; /** * NameID Format. */ - private String nameIdFormat; + private final String nameIdFormat; /** * nameId NameQualifier */ - private String nameIdNameQualifier; + private final String nameIdNameQualifier; /** * nameId SP NameQualifier */ - private String nameIdSPNameQualifier; + private final String nameIdSPNameQualifier; /** Create an empty set of logout request input parameters. */ public LogoutRequestParams() { + this(null, null); } /** @@ -118,45 +119,35 @@ protected LogoutRequestParams(LogoutRequestParams source) { /** * @return the name ID */ - protected String getNameId() { + public String getNameId() { return nameId; } - /** - * Sets the name ID - * - * @param nameId - * the name ID to set - */ - protected void setNameId(String nameId) { - this.nameId = nameId; - } - /** * @return the name ID format */ - protected String getNameIdFormat() { + public String getNameIdFormat() { return nameIdFormat; } /** * @return the name ID name qualifier */ - protected String getNameIdNameQualifier() { + public String getNameIdNameQualifier() { return nameIdNameQualifier; } /** * @return the name ID SP name qualifier */ - protected String getNameIdSPNameQualifier() { + public String getNameIdSPNameQualifier() { return nameIdSPNameQualifier; } /** * @return the session index */ - protected String getSessionIndex() { + public String getSessionIndex() { return sessionIndex; } } \ No newline at end of file diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index b3ae9c59..c81f172e 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -1095,11 +1095,13 @@ private static HttpRequest newHttpRequest(String requestURL, String samlRequestE @Test public void testPostProcessXml() throws Exception { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); - LogoutRequest logoutRequest = new LogoutRequest(settings) { + final LogoutRequestParams params = new LogoutRequestParams(); + LogoutRequest logoutRequest = new LogoutRequest(settings, params) { @Override - protected String postProcessXml(String logoutRequestXml, LogoutRequestParams params, Saml2Settings sett) { - assertEquals(logoutRequestXml, super.postProcessXml(logoutRequestXml, params, sett)); + protected String postProcessXml(String logoutRequestXml, LogoutRequestParams par, Saml2Settings sett) { + assertEquals(logoutRequestXml, super.postProcessXml(logoutRequestXml, par, sett)); assertSame(settings, sett); + assertSame(params, par); return "changed"; } }; From b54bff013678e03b6b4a7a4f0adf28e63c788be6 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 13 Aug 2021 22:41:57 +0200 Subject: [PATCH 102/133] Document the new API and extensibility features in README --- README.md | 76 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d1bf4d3b..321cfb64 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ The toolkit is hosted on github. You can download it from: The toolkit is hosted at [Sonatype OSSRH (OSS Repository Hosting)](http://central.sonatype.org/pages/ossrh-guide.html) that is synced to the Central Repository. Install it as a maven dependency: -``` +```xml com.onelogin java-saml @@ -441,7 +441,7 @@ If you want to use anything different than javax.servlet.http, you will need to #### Initiate SSO In order to send an AuthNRequest to the IdP: -``` +```java Auth auth = new Auth(request, response); auth.login(); ``` @@ -450,16 +450,18 @@ The AuthNRequest will be sent signed or unsigned based on the security settings The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. We can set a 'RelayState' parameter containing a return url to the login function: -``` +```java String returnUrl = 'https://example.com'; auth.login(relayState=returnUrl) ``` -The login method can receive 6 more optional parameters: -- *forceAuthn* When true the AuthNRequest will have the 'ForceAuthn' attribute set to 'true' -- *isPassive* When true the AuthNRequest will have the 'Ispassive' attribute set to 'true' -- *setNameIdPolicy* When true the AuthNRequest will set a nameIdPolicy element. +The login method can receive 3 more optional parameters: +- *authnRequestParams* which in turn allows to shape the AuthNRequest with the following properties: + - *forceAuthn* When true the AuthNRequest will have the `ForceAuthn` attribute set to `true` + - *isPassive* When true the AuthNRequest will have the `IsPassive` attribute set to `true` + - *setNameIdPolicy* When true the AuthNRequest will set a `NameIdPolicy` element + - *allowCreate* When true, and *setNameIdPolicy* is also true, the AuthNRequest will have the `AllowCreate` attribute set to `true` on the `NameIdPolicy` element + - *nameIdValueReq* Indicates to the IdP the subject that should be authenticated - *stay* Set to true to stay (returns the url string), otherwise set to false to execute a redirection to that url (IdP SSO URL) -- *nameIdValueReq* Indicates to the IdP the subject that should be authenticated - *parameters* Use it to send extra parameters in addition to the AuthNRequest By default, the login method initiates a redirect to the SAML Identity Provider. You can use the *stay* parameter, to prevent that, and execute the redirection manually. We need to use that if a match on the future SAMLResponse ID and the AuthNRequest ID to be sent is required. That AuthNRequest ID must be extracted and stored for future validation, so we can't execute the redirection on the login. Instead, set *stay* to true, then get that ID by @@ -474,7 +476,7 @@ Related to the SP there are 3 important endpoints: The metadata view, the ACS vi ##### SP Metadata This code will provide the XML metadata file of our SP, based on the info that we provided in the settings files. -``` +```java Auth auth = new Auth(); Saml2Settings settings = auth.getSettings(); String metadata = settings.getSPMetadata(); @@ -494,7 +496,7 @@ Before the XML metadata is exposed, a check takes place to ensure that the info ##### Attribute Consumer Service(ACS) This code handles the SAML response that the IdP forwards to the SP through the user's client. -``` +```java Auth auth = new Auth(request, response); auth.processResponse(); if (!auth.isAuthenticated()) { @@ -572,7 +574,7 @@ Before trying to get an attribute, check that the user is authenticated. If the ##### Single Logout Service (SLS) This code handles the Logout Request and the Logout Responses. -``` +```java Auth auth = new Auth(request, response); auth.processSLO(); List errors = auth.getErrors(); @@ -592,7 +594,7 @@ If we don't want that processSLO to destroy the session, pass the keepLocalSessi #### Initiate SLO In order to send a Logout Request to the IdP: -``` +```java Auth auth = new Auth(request, response); String nameId = null; @@ -615,36 +617,56 @@ String sessionIndex = null; if (session.getAttribute("sessionIndex") != null) { sessionIndex = session.getAttribute("sessionIndex").toString(); } -auth.logout(null, nameId, sessionIndex, nameIdFormat); -``` +auth.logout(null, new LogoutRequestParams(sessionIndex, nameId, nameIdFormat)); +```java The Logout Request will be sent signed or unsigned based on the security settings 'onelogin.saml2.security.logoutrequest_signed' The IdP will return the Logout Response through the user's client to the Single Logout Service of the SP. We can set a 'RelayState' parameter containing a return url to the login function: -``` +```java String returnUrl = 'https://example.com'; auth.logout(relayState=returnUrl) ``` -Also there are 7 optional parameters that can be set: -- nameId. That will be used to build the LogoutRequest. If not name_id parameter is set and the auth object processed a SAML Response with a NameId, then this NameId will be used. -- sessionIndex. Identifies the session of the user. -If a match on the LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored for future validation, we can get that ID by -- stay. True if we want to stay (returns the url string) False to execute a redirection to that url (IdP SLS URL) -- nameidFormat. The NameID Format that will be set in the LogoutRequest -- nameIdNameQualifier. The NameID NameQualifier that will be set in the LogoutRequest -- nameIdSPNameQualifier. The NameID SP Name Qualifier that will be set in the LogoutRequest -- parameters. Use it to send extra parameters in addition to the LogoutRequest - -By default the logout method initiates a redirect to the SAML Identity Provider. You can use the stay parameter, to prevent that, and execute the redirection manually. We need to use that +Also there are other 3 optional parameters that can be set: +- *logoutRequestParams* which in turn allows to shape the LogoutRequest with the following properties: + - *sessionIndex* Identifies the session of the user + - *nameId* That will be used to build the LogoutRequest. If no *nameId* parameter is set and the auth object processed a SAML Response with a `NameID`, then this `NameID` will be used + - *nameidFormat* The `NameID` `Format` that will be set on the LogoutRequest + - *nameIdNameQualifier* The `NameID` `NameQualifier` that will be set on the LogoutRequest + - *nameIdSPNameQualifier* The `NameID` `SPNameQualifier` that will be set on the LogoutRequest +- *stay* True if we want to stay (returns the url string) False to execute a redirection to that url (IdP SLS URL) +- *parameters* Use it to send extra parameters in addition to the LogoutRequest + +By default the logout method initiates a redirect to the SAML Identity Provider. You can use the *stay* parameter, to prevent that, and execute the redirection manually. We need to use that if a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must be extracted and stored for future validation so we can't execute the redirection on the logout, instead set stay to true, then get that ID by -``` +```java auth.getLastRequestId() ``` and later executing the redirection manually. +### Extending the provided implementation + +All the provided SAML message classes (`AuthnRequest`, `SamlResponse`, `LogoutRequest`, `LogoutResponse`) can be extended to add or change the processing behavior. + +In particular, the classes used to produce outgoing messages (`AuthnRequest`, `LogoutRequest`, and `LogoutResponse`) also provide a `postProcessXml` method that can be overridden to customise the generation of the corresponding SAML message XML, along with the ability to pass in proper extensions of the input parameter classes (`AuthnRequestParams`, `LogoutRequestParams`, and `LogoutResponseParams` respectively). + +Once you have prepared your extension classes, in order to make the `Auth` class use them, appropriate factories can then be +specified: + +```java +// let AuthnRequestEx, SamlResponseEx, LogoutRequestEx, LogoutResponseEx be your extension classes +Auth auth = new Auth(request, response); +auth.setAuthnRequestFactory(AuthnRequestEx::new); // to make it build custom AuthnRequests +auth.setSamlResponseFactory(SamlResponseEx::new); // to make it build custom SamlResponses +auth.setOutgoingLogoutRequestFactory(LogoutRequestEx::new); // to make it build custom outgoing LogoutRequests +auth.setReceivedLogoutRequestFactory(LogoutRequestEx::new); // to make it build custom incoming LogoutRequests +auth.setOutgoingLogoutResponseFactory(LogoutResponseEx::new); // to make it build custom outgoing LogoutResponses +auth.setReceivedLogoutResponseFactory(LogoutResponseEx::new); // to make it build custom incoming LogoutResponses +// then proceed with login/processResponse/logout/processSLO... +``` ### Working behind load balancer From 2917a41f6242554e412d446a092bdc0a59ba1f42 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Wed, 18 Aug 2021 18:02:37 +0200 Subject: [PATCH 103/133] Change factory approach to use just a single multi-message factory In this way the API of Auth gets simplified. --- README.md | 26 ++-- .../main/java/com/onelogin/saml2/Auth.java | 137 +++--------------- .../saml2/factory/SamlMessageFactory.java | 119 +++++++++++++++ .../factory/SamlOutgoingMessageFactory.java | 27 ---- .../factory/SamlReceivedMessageFactory.java | 28 ---- .../com/onelogin/saml2/test/AuthTest.java | 61 ++++++-- 6 files changed, 201 insertions(+), 197 deletions(-) create mode 100644 toolkit/src/main/java/com/onelogin/saml2/factory/SamlMessageFactory.java delete mode 100644 toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java delete mode 100644 toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java diff --git a/README.md b/README.md index 321cfb64..ac1fd237 100644 --- a/README.md +++ b/README.md @@ -653,19 +653,25 @@ All the provided SAML message classes (`AuthnRequest`, `SamlResponse`, `LogoutRe In particular, the classes used to produce outgoing messages (`AuthnRequest`, `LogoutRequest`, and `LogoutResponse`) also provide a `postProcessXml` method that can be overridden to customise the generation of the corresponding SAML message XML, along with the ability to pass in proper extensions of the input parameter classes (`AuthnRequestParams`, `LogoutRequestParams`, and `LogoutResponseParams` respectively). -Once you have prepared your extension classes, in order to make the `Auth` class use them, appropriate factories can then be -specified: +Once you have prepared your extension classes, in order to make the `Auth` class use them, an appropriate `SamlMessageFactory` implementation can be specified. As an example, assuming you've created two extension classes `AuthnRequestEx` and `SamlResponseEx` to customise the creation of AuthnRequest SAML messages and the validation of SAML responses respectively, as well as an extended `AuthnRequestParamsEx` input parameter class to drive the AuthnRequest generation post-processing, you can do the following: ```java -// let AuthnRequestEx, SamlResponseEx, LogoutRequestEx, LogoutResponseEx be your extension classes Auth auth = new Auth(request, response); -auth.setAuthnRequestFactory(AuthnRequestEx::new); // to make it build custom AuthnRequests -auth.setSamlResponseFactory(SamlResponseEx::new); // to make it build custom SamlResponses -auth.setOutgoingLogoutRequestFactory(LogoutRequestEx::new); // to make it build custom outgoing LogoutRequests -auth.setReceivedLogoutRequestFactory(LogoutRequestEx::new); // to make it build custom incoming LogoutRequests -auth.setOutgoingLogoutResponseFactory(LogoutResponseEx::new); // to make it build custom outgoing LogoutResponses -auth.setReceivedLogoutResponseFactory(LogoutResponseEx::new); // to make it build custom incoming LogoutResponses -// then proceed with login/processResponse/logout/processSLO... +auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public AuthnRequest createAuthnRequest(Saml2Settings settings, AuthnRequestParams params) { + return new AuthnRequestEx(settings, (AuthnRequestParamsEx) params); + } + + @Override + public SamlResponse createSamlResponse(Saml2Settings settings, HttpRequest request) throws Exception { + return new SamlResponseEx(settings, request); + } +}); +// then proceed with login... +auth.login(relayState, new AuthnRequestParamsEx()); // the custom generation of AuthnReqeustEx will be executed +// ... or process the response as usual +auth.processResponse(); // the custom validation of SamlResponseEx will be executed ``` ### Working behind load balancer diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index e0bb66c3..511914b8 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -26,8 +26,7 @@ import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.authn.SamlResponse; import com.onelogin.saml2.exception.SettingsException; -import com.onelogin.saml2.factory.SamlOutgoingMessageFactory; -import com.onelogin.saml2.factory.SamlReceivedMessageFactory; +import com.onelogin.saml2.factory.SamlMessageFactory; import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutRequest; @@ -171,19 +170,9 @@ public class Auth { */ private String lastResponse; - private static final SamlOutgoingMessageFactory DEFAULT_AUTHN_REQUEST_FACTORY = AuthnRequest::new; - private static final SamlReceivedMessageFactory DEFAULT_SAML_RESPONSE_FACTORY = SamlResponse::new; - private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; - private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY = LogoutRequest::new; - private static final SamlOutgoingMessageFactory DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY = LogoutResponse::new; - private static final SamlReceivedMessageFactory DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY = LogoutResponse::new; + private static final SamlMessageFactory DEFAULT_SAML_MESSAGE_FACTORY = new SamlMessageFactory() {}; - private SamlOutgoingMessageFactory authnRequestFactory = DEFAULT_AUTHN_REQUEST_FACTORY; - private SamlReceivedMessageFactory samlResponseFactory = DEFAULT_SAML_RESPONSE_FACTORY; - private SamlOutgoingMessageFactory outgoingLogoutRequestFactory = DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY; - private SamlReceivedMessageFactory receivedLogoutRequestFactory = DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY; - private SamlOutgoingMessageFactory outgoingLogoutResponseFactory = DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; - private SamlReceivedMessageFactory receivedLogoutResponseFactory = DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY; + private SamlMessageFactory samlMessageFactory = DEFAULT_SAML_MESSAGE_FACTORY; /** * Initializes the SP SAML instance. @@ -626,7 +615,7 @@ public String login(String relayState, AuthnRequestParams authnRequestParams, Bo * @throws SettingsException */ public String login(String relayState, AuthnRequestParams authnRequestParams, Boolean stay, Map parameters) throws IOException, SettingsException { - AuthnRequest authnRequest = authnRequestFactory.create(settings, authnRequestParams); + AuthnRequest authnRequest = samlMessageFactory.createAuthnRequest(settings, authnRequestParams); if (parameters == null) { parameters = new HashMap(); @@ -802,7 +791,7 @@ public String logout(String relayState, LogoutRequestParams logoutRequestParams, parameters = new HashMap(); } - LogoutRequest logoutRequest = outgoingLogoutRequestFactory.create(settings, logoutRequestParams); + LogoutRequest logoutRequest = samlMessageFactory.createOutgoingLogoutRequest(settings, logoutRequestParams); String samlLogoutRequest = logoutRequest.getEncodedLogoutRequest(); parameters.put("SAMLRequest", samlLogoutRequest); @@ -1213,7 +1202,7 @@ public void processResponse(String requestId) throws Exception { final String samlResponseParameter = httpRequest.getParameter("SAMLResponse"); if (samlResponseParameter != null) { - SamlResponse samlResponse = samlResponseFactory.create(settings, httpRequest); + SamlResponse samlResponse = samlMessageFactory.createSamlResponse(settings, httpRequest); lastResponse = samlResponse.getSAMLResponseXml(); if (samlResponse.isValid(requestId)) { @@ -1286,7 +1275,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta final String samlResponseParameter = httpRequest.getParameter("SAMLResponse"); if (samlResponseParameter != null) { - LogoutResponse logoutResponse = receivedLogoutResponseFactory.create(settings, httpRequest); + LogoutResponse logoutResponse = samlMessageFactory.createIncomingLogoutResponse(settings, httpRequest); lastResponse = logoutResponse.getLogoutResponseXml(); if (!logoutResponse.isValid(requestId)) { errors.add("invalid_logout_response"); @@ -1316,7 +1305,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } return null; } else if (samlRequestParameter != null) { - LogoutRequest logoutRequest = receivedLogoutRequestFactory.create(settings, httpRequest); + LogoutRequest logoutRequest = samlMessageFactory.createIncomingLogoutRequest(settings, httpRequest); lastRequest = logoutRequest.getLogoutRequestXml(); if (!logoutRequest.isValid()) { errors.add("invalid_logout_request"); @@ -1334,7 +1323,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } String inResponseTo = logoutRequest.id; - LogoutResponse logoutResponseBuilder = outgoingLogoutResponseFactory.create(settings, + LogoutResponse logoutResponseBuilder = samlMessageFactory.createOutgoingLogoutResponse(settings, new LogoutResponseParams(inResponseTo, Constants.STATUS_SUCCESS)); lastResponse = logoutResponseBuilder.getLogoutResponseXml(); @@ -1663,107 +1652,19 @@ public String getLastResponseXML() { } /** - * Sets the factory this {@link Auth} will use to create {@link AuthnRequest} - * objects. + * Sets the factory this {@link Auth} will use to create SAML messages. *

- * This allows consumers to provide their own extension of {@link AuthnRequest} - * possibly implementing custom features and/or XML post-processing. + * This allows consumers to provide their own extension classes for SAML message + * XML generation and/or processing. * - * @param authnRequestFactory - * the factory to use to create {@link AuthnRequest} objects; if + * @param samlMessageFactory + * the factory to use to create SAML message objects; if * null, a default provider will be used which creates - * plain {@link AuthnRequest} instances + * the standard message implementation provided by this library + * (i.e.: {@link AuthnRequest}, {@link SamlResponse}, + * {@link LogoutRequest} and {@link LogoutResponse}) */ - public void setAuthnRequestFactory( - final SamlOutgoingMessageFactory authnRequestFactory) { - this.authnRequestFactory = authnRequestFactory != null ? authnRequestFactory - : DEFAULT_AUTHN_REQUEST_FACTORY; - } - - /** - * Sets the factory this {@link Auth} will use to create {@link SamlResponse} - * objects. - *

- * This allows consumers to provide their own extension of {@link SamlResponse} - * possibly implementing custom features and/or XML validation. - * - * @param samlResponseFactory - * the factory to use to create {@link SamlResponse} objects; if - * null, a default factory will be used which creates - * plain {@link SamlResponse} instances - */ - public void setSamlResponseFactory(final SamlReceivedMessageFactory samlResponseFactory) { - this.samlResponseFactory = samlResponseFactory != null? samlResponseFactory: DEFAULT_SAML_RESPONSE_FACTORY; - } - - /** - * Sets the factory this {@link Auth} will use to create outgoing - * {@link LogoutRequest} objects. - *

- * This allows consumers to provide their own extension of {@link LogoutRequest} - * possibly implementing custom features and/or XML post-processing. - * - * @param outgoingLogoutRequestFactory - * the factory to use to create outgoing {@link LogoutRequest} - * objects; if null, a default provider will be used - * which creates plain {@link LogoutRequest} instances - */ - public void setOutgoingLogoutRequestFactory(final - SamlOutgoingMessageFactory outgoingLogoutRequestFactory) { - this.outgoingLogoutRequestFactory = outgoingLogoutRequestFactory != null? outgoingLogoutRequestFactory: DEFAULT_OUTGOING_LOGOUT_REQUEST_FACTORY; - } - - /** - * Sets the factory this {@link Auth} will use to create received - * {@link LogoutRequest} objects. - *

- * This allows consumers to provide their own extension of {@link LogoutRequest} - * possibly implementing custom features and/or XML validation. - * - * @param receivedLogoutRequestFactory - * the factory to use to create received {@link LogoutRequest} - * objects; if null, a default provider will be used - * which creates plain {@link LogoutRequest} instances - */ - public void setReceivedLogoutRequestFactory( - final SamlReceivedMessageFactory receivedLogoutRequestFactory) { - this.receivedLogoutRequestFactory = receivedLogoutRequestFactory != null ? receivedLogoutRequestFactory - : DEFAULT_RECEIVED_LOGOUT_REQUEST_FACTORY; - } - - /** - * Sets the factory this {@link Auth} will use to create outgoing - * {@link LogoutResponse} objects. - *

- * This allows consumers to provide their own extension of - * {@link LogoutResponse} possibly implementing custom features and/or XML - * post-processing. - * - * @param outgoingLogoutResponseFactory - * the factory to use to create outgoing {@link LogoutResponse} - * objects; if null, a default provider will be used - * which creates plain {@link LogoutResponse} instances - */ - public void setOutgoingLogoutResponseFactory(final - SamlOutgoingMessageFactory outgoingLogoutResponseFactory) { - this.outgoingLogoutResponseFactory = outgoingLogoutResponseFactory != null? outgoingLogoutResponseFactory: DEFAULT_OUTGOING_LOGOUT_RESPONSE_FACTORY; - } - - /** - * Sets the factory this {@link Auth} will use to create received - * {@link LogoutResponse} objects. - *

- * This allows consumers to provide their own extension of - * {@link LogoutResponse} possibly implementing custom features and/or XML - * validation. - * - * @param receivedLogoutResponseFactory - * the factory to use to create received {@link LogoutResponse} - * objects; if null, a default provider will be used - * which creates plain {@link LogoutResponse} instances - */ - public void setReceivedLogoutResponseFactory(final - SamlReceivedMessageFactory receivedLogoutResponseFactory) { - this.receivedLogoutResponseFactory = receivedLogoutResponseFactory != null? receivedLogoutResponseFactory: DEFAULT_RECEIVED_LOGOUT_RESPONSE_FACTORY; + public void setSamlMessageFactory(final SamlMessageFactory samlMessageFactory) { + this.samlMessageFactory = samlMessageFactory != null ? samlMessageFactory : DEFAULT_SAML_MESSAGE_FACTORY; } } diff --git a/toolkit/src/main/java/com/onelogin/saml2/factory/SamlMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlMessageFactory.java new file mode 100644 index 00000000..08c71e91 --- /dev/null +++ b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlMessageFactory.java @@ -0,0 +1,119 @@ +package com.onelogin.saml2.factory; + +import com.onelogin.saml2.Auth; +import com.onelogin.saml2.authn.AuthnRequest; +import com.onelogin.saml2.authn.AuthnRequestParams; +import com.onelogin.saml2.authn.SamlResponse; +import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.logout.LogoutRequest; +import com.onelogin.saml2.logout.LogoutRequestParams; +import com.onelogin.saml2.logout.LogoutResponse; +import com.onelogin.saml2.logout.LogoutResponseParams; +import com.onelogin.saml2.settings.Saml2Settings; + +/** + * Factory which can create all kind of SAML message objects. + *

+ * One such factory is used by the {@link Auth} class to orchestrate login and + * logout operations. + *

+ * Default implementations for all creation methods are provided: they create + * instances of the standard classes provided by the library. Any extension + * class may simply override the desired creation methods in order to return + * instances of custom extensions of those standard classes. + */ +public interface SamlMessageFactory { + + /** + * Creates an {@link AuthnRequest} instance. + * + * @param settings + * the settings + * @param params + * the authentication request input parameters + * @return the created {@link AuthnRequest} instance + */ + default AuthnRequest createAuthnRequest(final Saml2Settings settings, final AuthnRequestParams params) { + return new AuthnRequest(settings, params); + } + + /** + * Creates a {@link SamlResponse} instance. + * + * @param settings + * the settings + * @param request + * the HTTP request from which the response is to be extracted and + * parsed + * @return the created {@link SamlResponse} instance + * @throws Exception + * in case some error occurred while trying to create the + * {@link SamlResponse} instance + */ + default SamlResponse createSamlResponse(final Saml2Settings settings, final HttpRequest request) + throws Exception { + return new SamlResponse(settings, request); + } + + /** + * Creates a {@link LogoutRequest} instance for an outgoing request. + * + * @param settings + * the settings + * @param params + * the logout request input parameters + * @return the created {@link LogoutRequest} instance + */ + default LogoutRequest createOutgoingLogoutRequest(final Saml2Settings settings, final LogoutRequestParams params) { + return new LogoutRequest(settings, params); + } + + /** + * Creates a {@link LogoutRequest} instance for an incoming request. + * + * @param settings + * the settings + * @param request + * the HTTP request from which the logout request is to be + * extracted and parsed + * @return the created {@link LogoutRequest} instance + * @throws Exception + * in case some error occurred while trying to create the + * {@link LogoutRequest} instance + */ + default LogoutRequest createIncomingLogoutRequest(final Saml2Settings settings, final HttpRequest request) + throws Exception { + return new LogoutRequest(settings, request); + } + + /** + * Creates a {@link LogoutResponse} instance for an outgoing response. + * + * @param settings + * the settings + * @param params + * the logout response input parameters + * @return the created {@link LogoutResponse} instance + */ + default LogoutResponse createOutgoingLogoutResponse(final Saml2Settings settings, final LogoutResponseParams params) { + return new LogoutResponse(settings, params); + } + + /** + * Creates a {@link LogoutRequest} instance for an incoming response. + * + * @param settings + * the settings + * @param request + * the HTTP request from which the logout response is to be + * extracted and parsed + * @return the created {@link LogoutResponse} instance + * @throws Exception + * in case some error occurred while trying to create the + * {@link LogoutResponse} instance + */ + default LogoutResponse createIncomingLogoutResponse(final Saml2Settings settings, final HttpRequest request) + throws Exception { + return new LogoutResponse(settings, request); + } +} \ No newline at end of file diff --git a/toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java deleted file mode 100644 index 7cbdd32c..00000000 --- a/toolkit/src/main/java/com/onelogin/saml2/factory/SamlOutgoingMessageFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.onelogin.saml2.factory; - -import com.onelogin.saml2.settings.Saml2Settings; - -/** - * Factory which can create an outgoing SAML message object from a - * {@link Saml2Settings} instance and other input parameters. - * - * @param - * the type of input parameters required - * @param - * the type of SAML outgoing message object created - */ -@FunctionalInterface -public interface SamlOutgoingMessageFactory { - - /** - * Creates an outgoing SAML message object. - * - * @param settings - * the settings - * @param params - * the input parameters - * @return the created received SAML message object - */ - R create(Saml2Settings settings, U params); -} \ No newline at end of file diff --git a/toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java b/toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java deleted file mode 100644 index 0a78a800..00000000 --- a/toolkit/src/main/java/com/onelogin/saml2/factory/SamlReceivedMessageFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.onelogin.saml2.factory; - -import com.onelogin.saml2.http.HttpRequest; -import com.onelogin.saml2.settings.Saml2Settings; - -/** - * Factory which can create a received SAML message object from a - * {@link Saml2Settings} instance and other input parameters. - * - * @param - * the type of received SAML message object created - */ -@FunctionalInterface -public interface SamlReceivedMessageFactory { - - /** - * Creates a received SAML message object. - * - * @param settings - * the settings - * @param httpRequest - * the HTTP request - * @return the created received SAML message object - * @throws Exception - * if the message creation fails - */ - R create(Saml2Settings settings, HttpRequest httpRequest) throws Exception; -} \ No newline at end of file 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 6846bfa7..6c376839 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -55,6 +55,7 @@ import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.exception.XMLEntityException; +import com.onelogin.saml2.factory.SamlMessageFactory; import com.onelogin.saml2.http.HttpRequest; import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutRequestParams; @@ -2235,7 +2236,7 @@ private static class FactoryInvokedException extends RuntimeException { } /** - * Tests that the AuthnRequest factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for AuthnRequests and the right parameters are passed to it. * * @throws Exception * @@ -2259,12 +2260,17 @@ public AuthnRequestEx(Saml2Settings sett, AuthnRequestParams par) { } Auth auth = new Auth(settings, request, response); - auth.setAuthnRequestFactory((sett, param) -> new AuthnRequestEx(sett, param)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public AuthnRequest createAuthnRequest(Saml2Settings settings, AuthnRequestParams params) { + return new AuthnRequestEx(settings, params); + } + }); auth.login(params); } /** - * Tests that the SamlResponse factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for SamlResponses and the right parameters are passed to it. * * @throws Exception * @@ -2291,12 +2297,17 @@ public SamlResponseEx(Saml2Settings sett, HttpRequest req) throws Exception { } Auth auth = new Auth(settings, request, response); - auth.setSamlResponseFactory((sett, req) -> new SamlResponseEx(sett, req)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public SamlResponse createSamlResponse(Saml2Settings settings, HttpRequest request) throws Exception { + return new SamlResponseEx(settings, request); + } + }); auth.processResponse(); } /** - * Tests that the outgoing LogoutRequest factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for outgoing LogoutRequests and the right parameters are passed to it. * * @throws Exception * @@ -2322,19 +2333,24 @@ public LogoutRequestEx(Saml2Settings sett, LogoutRequestParams par) { } Auth auth = new Auth(settings, request, response); - auth.setOutgoingLogoutRequestFactory((sett, param) -> new LogoutRequestEx(sett, param)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public LogoutRequest createOutgoingLogoutRequest(Saml2Settings settings, LogoutRequestParams params) { + return new LogoutRequestEx(settings, params); + } + }); auth.logout(null, params); } /** - * Tests that the received LogoutRequest factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for incoming LogoutRequests and the right parameters are passed to it. * * @throws Exception * * @see com.onelogin.saml2.Auth#setReceivedLogoutRequestFactory(com.onelogin.saml2.factory.SamlReceivedMessageFactory) */ @Test(expected = FactoryInvokedException.class) - public void testReceivedLogoutRequestFactory() throws Exception { + public void testIncomingLogoutRequestFactory() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); HttpSession session = mock(HttpSession.class); @@ -2357,12 +2373,18 @@ public LogoutRequestEx(Saml2Settings sett, HttpRequest req) { } Auth auth = new Auth(settings, request, response); - auth.setReceivedLogoutRequestFactory((sett, req) -> new LogoutRequestEx(sett, req)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public LogoutRequest createIncomingLogoutRequest(Saml2Settings settings, HttpRequest request) + throws Exception { + return new LogoutRequestEx(settings, request); + } + }); auth.processSLO(); } /** - * Tests that the outgoing LogoutResponse factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for outgoing LogoutResponses and the right parameters are passed to it. * * @throws Exception * @@ -2396,19 +2418,25 @@ public LogoutResponseEx(Saml2Settings sett, LogoutResponseParams par) { } Auth auth = new Auth(settings, request, response); - auth.setOutgoingLogoutResponseFactory((sett, param) -> new LogoutResponseEx(settings, param)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public LogoutResponse createOutgoingLogoutResponse(Saml2Settings settings, + LogoutResponseParams params) { + return new LogoutResponseEx(settings, params); + } + }); auth.processSLO(false, null); } /** - * Tests that the received LogoutResponse factory gets invoked by Auth and the right parameters are passed to it. + * Tests that the SAML message factory gets invoked by Auth for incoming LogoutResponses and the right parameters are passed to it. * * @throws Exception * * @see com.onelogin.saml2.Auth#setReceivedLogoutResponseFactory(com.onelogin.saml2.factory.SamlReceivedMessageFactory) */ @Test(expected = FactoryInvokedException.class) - public void testReceivedLogoutResponseFactory() throws Exception { + public void testIncomingLogoutResponseFactory() throws Exception { HttpServletRequest request = mock(HttpServletRequest.class); HttpServletResponse response = mock(HttpServletResponse.class); HttpSession session = mock(HttpSession.class); @@ -2431,7 +2459,12 @@ public LogoutResponseEx(Saml2Settings sett, HttpRequest req) { } Auth auth = new Auth(settings, request, response); - auth.setReceivedLogoutResponseFactory((sett, req) -> new LogoutResponseEx(sett, req)); + auth.setSamlMessageFactory(new SamlMessageFactory() { + @Override + public LogoutResponse createIncomingLogoutResponse(Saml2Settings settings, HttpRequest request) { + return new LogoutResponseEx(settings, request); + } + }); auth.processSLO(); } } From c939096310049fc3c37d696fa11591ad6b3919cc Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 21 Sep 2021 19:18:08 +0200 Subject: [PATCH 104/133] Update xmlsec dependency --- README.md | 5 ++++- core/pom.xml | 2 +- toolkit/pom.xml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d1bf4d3b..25c06020 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,13 @@ Add SAML support to your Java applications using this library. Forget those complicated libraries and use that open source library provided and supported by OneLogin Inc. +2.8.0 uses xmlsec 2.2.3 which fixes [CVE-2021-40690](https://snyk.io/vuln/SNYK-JAVA-ORGAPACHESANTUARIO-1655558) + Version >= 2.5.0 compatible with java8 / java9. Not compatible with java7 + 2.5.0 sets the 'strict' setting parameter to true. -2.5.0 uses xmlsec 2.1.4 which fixes [CVE-2019-12400](https://snyk.io/vuln/SNYK-JAVA-ORGAPACHESANTUARIO-460281) +2.5.0 uses xmlsec 2.1.4 which fixes [CVE-2019-12400](https://snyk.io/vuln/SNYK-JAVA-ORGAPACHESANTUARIO-460281) Version 2.0.0 - 2.4.0, compatible with java7 / java8. diff --git a/core/pom.xml b/core/pom.xml index 405ea7f1..e2ce82af 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -60,7 +60,7 @@ org.apache.santuario xmlsec - 2.2.2 + 2.2.3 commons-codec diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 841ee89c..f2323afb 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -81,7 +81,7 @@ org.apache.santuario xmlsec - 2.2.2 + 2.2.3 commons-codec From 95dd5dd1253d23452a3ecc389077b86692e19da0 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 21 Sep 2021 21:28:27 +0200 Subject: [PATCH 105/133] Update rest of dependencies --- core/pom.xml | 6 +++--- pom.xml | 17 ++++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e2ce82af..11aed629 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,13 +72,13 @@ com.azure azure-security-keyvault-keys - 4.3.0 + 4.3.3 true com.azure azure-identity - 1.3.3 + 1.3.6 true @@ -88,7 +88,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.7 jacoco.agent.argLine diff --git a/pom.xml b/pom.xml index b4bee9bb..456a455f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ 4.0.0 - com.onelogin java-saml-toolkit 2.7.1-SNAPSHOT @@ -13,10 +12,10 @@ UTF-8 UTF-8 - 1.7.30 - 4.13.1 - 1.2.3 - 3.11 + 1.7.32 + 4.13.2 + 1.2.6 + 3.12.0 @@ -72,7 +71,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 1.4.1 + 3.0.0 enforce-versions @@ -97,7 +96,7 @@ org.owasp dependency-check-maven - 6.1.6 + 6.3.1 7 @@ -198,7 +197,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.3.1 attach-javadocs @@ -214,7 +213,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts From c71f75bd9ad4552f02cb5345e6ae26dbce0fdab4 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 21 Sep 2021 21:33:16 +0200 Subject: [PATCH 106/133] [maven-release-plugin] prepare release v2.8.0 --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 11aed629..0855e38b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.1-SNAPSHOT + 2.8.0 jar diff --git a/pom.xml b/pom.xml index 456a455f..5e2896c1 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.onelogin java-saml-toolkit - 2.7.1-SNAPSHOT + 2.8.0 pom OneLogin java-saml Toolkit Project @@ -157,7 +157,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - HEAD + v2.8.0 diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 6ca21609..73866caa 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.7.1-SNAPSHOT + 2.8.0 java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 4f532de0..3dc9212d 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.1-SNAPSHOT + 2.8.0 java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index f2323afb..d6d5b4be 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.7.1-SNAPSHOT + 2.8.0 jar From 265c5267f09f9c207972c2771a7f5dff4a66d27c Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 21 Sep 2021 21:33:21 +0200 Subject: [PATCH 107/133] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 0855e38b..a00edff3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.0 + 2.8.1-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index 5e2896c1..f18ede37 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.onelogin java-saml-toolkit - 2.8.0 + 2.8.1-SNAPSHOT pom OneLogin java-saml Toolkit Project @@ -157,7 +157,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - v2.8.0 + HEAD diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 73866caa..9c6185e3 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.8.0 + 2.8.1-SNAPSHOT java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 3dc9212d..470d0048 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.0 + 2.8.1-SNAPSHOT java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index d6d5b4be..e517b4de 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.0 + 2.8.1-SNAPSHOT jar From f7364f7d7c98a4d6da800d4659ca5f2783d4d216 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 6 Apr 2021 19:33:50 +0200 Subject: [PATCH 108/133] Add support for multiple fully-fledged contacts for SP It's now possible to specify an arbitrary number of contacts, with all the information supported by the SAML 2.0 specification, including multiple e-mail addresses and multiple phone numbers per contact. Since this is indeed information related to the Service Provider (which appears in the SP metadata), the new (indexed) properties used to declare contacts have been put in the "onelogin.saml2.sp" namespace and their parsing uses the same technique used to support multiple Attribute Consuming Services. The legacy way to specify just a technical and a support contacts is still supported (these "legacy" contacts are appended as the last two ones). The Contact class has been enhanced to support all ContactType attributes supported by the SAML 2.0 specification but still exposes the old constructor and single e-mail address getter (although being deprecated) to provide a 100% backward compatible API. Fixes #326. --- README.md | 37 ++- .../com/onelogin/saml2/model/Contact.java | 108 ++++++-- .../com/onelogin/saml2/settings/Metadata.java | 17 +- .../saml2/settings/Saml2Settings.java | 7 +- .../saml2/settings/SettingsBuilder.java | 232 +++++++++++++++++- .../com/onelogin/saml2/util/Constants.java | 9 +- .../test/settings/IdPMetadataParserTest.java | 18 +- .../saml2/test/settings/MetadataTest.java | 73 +++++- .../test/settings/SettingBuilderTest.java | 166 +++++++++++-- .../resources/config/config.all.properties | 13 + .../config/config.all_specialchars.properties | 13 + 11 files changed, 620 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index a94c8ffe..df0c3bf0 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,31 @@ onelogin.saml2.sp.x509certNew = # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = +# Organization +onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.displayname = SP Java Example +onelogin.saml2.organization.url = http://sp.example.com +onelogin.saml2.organization.lang = en + +# Contacts (use indexes to specify multiple contacts, multiple e-mail addresses per contact, multiple phone numbers per contact) +onelogin.saml2.sp.contact[0].contactType=administrative +onelogin.saml2.sp.contact[0].company=ACME +onelogin.saml2.sp.contact[0].given_name=Guy +onelogin.saml2.sp.contact[0].sur_name=Administrative +onelogin.saml2.sp.contact[0].email_address[0]=administrative@example.com +onelogin.saml2.sp.contact[0].email_address[1]=administrative2@example.com +onelogin.saml2.sp.contact[0].telephone_number[0]=+1-123456789 +onelogin.saml2.sp.contact[0].telephone_number[1]=+1-987654321 +onelogin.saml2.sp.contact[1].contactType=other +onelogin.saml2.sp.contact[1].company=Big Corp +onelogin.saml2.sp.contact[1].email_address=info@example.com + +# Legacy contacts (legacy way to specify just a technical and a support contact with minimal info) +onelogin.saml2.contacts.technical.given_name = Technical Guy +onelogin.saml2.contacts.technical.email_address = technical@example.com +onelogin.saml2.contacts.support.given_name = Support Guy +onelogin.saml2.contacts.support.email_address = support@example.com + ## Identity Provider Data that we want connect with our SP ## # Identifier of the IdP entity (must be a URI) @@ -374,18 +399,6 @@ onelogin.saml2.security.reject_deprecated_alg = true onelogin.saml2.parsing.trim_name_ids = false onelogin.saml2.parsing.trim_attribute_values = false -# Organization -onelogin.saml2.organization.name = SP Java -onelogin.saml2.organization.displayname = SP Java Example -onelogin.saml2.organization.url = http://sp.example.com -onelogin.saml2.organization.lang = en - -# Contacts -onelogin.saml2.contacts.technical.given_name = Technical Guy -onelogin.saml2.contacts.technical.email_address = technical@example.com -onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com - # Prefix used in generated Unique IDs. # Optional, defaults to ONELOGIN_ or full ID is like ONELOGIN_ebb0badd-4f60-4b38-b20a-a8e01f0592b1. # At minimun, the prefix can be non-numeric character such as "_". diff --git a/core/src/main/java/com/onelogin/saml2/model/Contact.java b/core/src/main/java/com/onelogin/saml2/model/Contact.java index 82e3da22..eb9a52ee 100644 --- a/core/src/main/java/com/onelogin/saml2/model/Contact.java +++ b/core/src/main/java/com/onelogin/saml2/model/Contact.java @@ -1,5 +1,8 @@ package com.onelogin.saml2.model; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Contact class of OneLogin's Java Toolkit. @@ -8,34 +11,78 @@ */ public class Contact { /** - * Contact type - */ + * Contact type + */ private final String contactType; /** - * Contact given name - */ + * Contact company + */ + private final String company; + + /** + * Contact given name + */ private final String givenName; + + /** + * Contact surname + */ + private final String surName; /** - * Contact email - */ - private final String emailAddress; + * Contact email + */ + private final List emailAddresses; /** - * Constructor + * Contact phone number + */ + private final List telephoneNumbers; + + /** + * Constructor to specify minimal contact data. + *

+ * To maintain backward compatibility, a null given name and a + * null e-mail address are handled as being empty strings. * * @param contactType - * String. Contact type + * Contact type * @param givenName - * String. Contact given name + * Contact given name * @param emailAddress - * String. Contact email + * Contact e-mail + * @deprecated use {@link #Contact(String, String, String, String, List, List)} */ + @Deprecated public Contact(String contactType, String givenName, String emailAddress) { + this(contactType, null, givenName != null ? givenName : "", null, + Arrays.asList(emailAddress != null ? emailAddress : ""), null); + } + + /** + * Constructor + * + * @param contactType + * Contact type + * @param givenName + * Contact given name + * @param surName + * Contact surname + * @param company + * Contact company + * @param emailAddresses + * Contact e-mails + * @param telephoneNumbers + * Contact phone numbers + */ + public Contact(String contactType, String company, String givenName, String surName, List emailAddresses, List telephoneNumbers) { this.contactType = contactType != null? contactType : ""; - this.givenName = givenName != null? givenName : ""; - this.emailAddress = emailAddress != null? emailAddress : ""; + this.company = company; + this.givenName = givenName; + this.surName = surName; + this.emailAddresses = emailAddresses != null? emailAddresses: Collections.emptyList(); + this.telephoneNumbers = telephoneNumbers != null? telephoneNumbers: Collections.emptyList(); } /** @@ -46,17 +93,46 @@ public final String getContactType() { } /** - * @return string the contact email + * @return the contact email + * @deprecated this returns just the first e-mail address in {@link #getEmailAddresses()} */ + @Deprecated public final String getEmailAddress() { - return emailAddress; + return emailAddresses.size() > 0? emailAddresses.get(0): null; } /** - * @return string the contact given name + * @return a list containing the contact e-mail addresses (never null) + */ + public final List getEmailAddresses() { + return emailAddresses; + } + + /** + * @return the contact given name */ public final String getGivenName() { return givenName; } + + /** + * @return the contact surname + */ + public final String getSurName() { + return surName; + } + + /** + * @return the contact company + */ + public final String getCompany() { + return company; + } + /** + * @return a list containing the contact phone numbers (never null) + */ + public final List getTelephoneNumbers() { + return telephoneNumbers; + } } \ No newline at end of file diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 5ed070a6..30a83184 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -277,8 +277,21 @@ private String toContactsXml(List contacts) { for (Contact contact : contacts) { contactsXml.append(""); - contactsXml.append("" + Util.toXml(contact.getGivenName()) + ""); - contactsXml.append("" + Util.toXml(contact.getEmailAddress()) + ""); + final String company = contact.getCompany(); + if(company != null) + contactsXml.append("" + Util.toXml(company) + ""); + final String givenName = contact.getGivenName(); + if(givenName != null) + contactsXml.append("" + Util.toXml(givenName) + ""); + final String surName = contact.getSurName(); + if(surName != null) + contactsXml.append("" + Util.toXml(surName) + ""); + final List emailAddresses = contact.getEmailAddresses(); + emailAddresses.forEach(emailAddress -> contactsXml + .append("" + Util.toXml(emailAddress) + "")); + final List telephoneNumbers = contact.getTelephoneNumbers(); + telephoneNumbers.forEach(telephoneNumber -> contactsXml + .append("" + Util.toXml(telephoneNumber) + "")); contactsXml.append(""); } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index b2d2078a..292b75e6 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -9,6 +9,8 @@ import java.util.List; import com.onelogin.saml2.model.hsm.HSM; + +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -1032,7 +1034,10 @@ public List checkSPSettings() { } */ - if (contact.getEmailAddress().isEmpty() || contact.getGivenName().isEmpty()) { + if (contact.getEmailAddresses().isEmpty() || contact.getEmailAddresses().stream().allMatch(StringUtils::isEmpty) || + (StringUtils.isEmpty(contact.getCompany()) && + StringUtils.isEmpty(contact.getGivenName()) && + StringUtils.isEmpty(contact.getSurName()))) { errorMsg = "contact_not_enough_data"; errors.add(errorMsg); LOGGER.error(errorMsg); diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 6c7a52ff..6232044e 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -15,11 +15,19 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; import java.util.Properties; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +35,7 @@ import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.model.Organization; +import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.Util; /** @@ -65,6 +74,14 @@ public class SettingsBuilder { public final static String SP_PRIVATEKEY_PROPERTY_KEY = "onelogin.saml2.sp.privatekey"; public final static String SP_X509CERTNEW_PROPERTY_KEY = "onelogin.saml2.sp.x509certNew"; + public final static String SP_CONTACT_PROPERTY_KEY_PREFIX = "onelogin.saml2.sp.contact"; + public final static String SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX = "contactType"; + public final static String SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX = "company"; + public final static String SP_CONTACT_GIVEN_NAME_PROPERTY_KEY_SUFFIX = "given_name"; + public final static String SP_CONTACT_SUR_NAME_PROPERTY_KEY_SUFFIX = "sur_name"; + public final static String SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX = "email_address"; + public final static String SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX = "telephone_number"; + // KeyStore public final static String KEYSTORE_KEY = "onelogin.saml2.keystore.store"; public final static String KEYSTORE_ALIAS = "onelogin.saml2.keystore.alias"; @@ -112,9 +129,13 @@ public class SettingsBuilder { public final static String PARSING_TRIM_ATTRIBUTE_VALUES = "onelogin.saml2.parsing.trim_attribute_values"; // Misc + @Deprecated public final static String CONTACT_TECHNICAL_GIVEN_NAME = "onelogin.saml2.contacts.technical.given_name"; + @Deprecated public final static String CONTACT_TECHNICAL_EMAIL_ADDRESS = "onelogin.saml2.contacts.technical.email_address"; + @Deprecated public final static String CONTACT_SUPPORT_GIVEN_NAME = "onelogin.saml2.contacts.support.given_name"; + @Deprecated public final static String CONTACT_SUPPORT_EMAIL_ADDRESS = "onelogin.saml2.contacts.support.email_address"; public final static String ORGANIZATION_NAME = "onelogin.saml2.organization.name"; @@ -261,7 +282,7 @@ public Saml2Settings build(Saml2Settings saml2Setting) { List contacts = this.loadContacts(); if (!contacts.isEmpty()) { - saml2Setting.setContacts(loadContacts()); + saml2Setting.setContacts(contacts); } Organization org = this.loadOrganization(); @@ -464,15 +485,26 @@ private Organization loadOrganization() { /** * Loads the contacts settings from the properties file + * + * @return a list containing all the loaded contacts */ + @SuppressWarnings("deprecation") private List loadContacts() { - List contacts = new LinkedList<>(); - + // first split properties into a map of properties + // key = contact index; value = contact properties + final SortedMap> contactProps = + extractIndexedProperties(SP_CONTACT_PROPERTY_KEY_PREFIX, samlData); + // then build each contact + // multiple indexed services specified + final List contacts = contactProps.entrySet().stream() + .map(entry -> loadContact(entry.getValue(), entry.getKey())) + .collect(Collectors.toList()); + // append legacy contacts if present String technicalGn = loadStringProperty(CONTACT_TECHNICAL_GIVEN_NAME); String technicalEmailAddress = loadStringProperty(CONTACT_TECHNICAL_EMAIL_ADDRESS); if ((technicalGn != null && !technicalGn.isEmpty()) || (technicalEmailAddress != null && !technicalEmailAddress.isEmpty())) { - Contact technical = new Contact("technical", technicalGn, technicalEmailAddress); + Contact technical = new Contact(Constants.CONTACT_TYPE_TECHNICAL, technicalGn, technicalEmailAddress); contacts.add(technical); } @@ -480,13 +512,185 @@ private List loadContacts() { String supportEmailAddress = loadStringProperty(CONTACT_SUPPORT_EMAIL_ADDRESS); if ((supportGn != null && !supportGn.isEmpty()) || (supportEmailAddress != null && !supportEmailAddress.isEmpty())) { - Contact support = new Contact("support", supportGn, supportEmailAddress); + Contact support = new Contact(Constants.CONTACT_TYPE_SUPPORT, supportGn, supportEmailAddress); contacts.add(support); } return contacts; } + /** + * Loads a single contact from settings. + * + * @param contactProps + * a map containing the contact settings + * @param index + * the contact index + * @return the loaded contact + */ + private Contact loadContact(Map contactProps, int index) { + final String contactType = loadStringProperty(SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX, contactProps); + final String company = loadStringProperty(SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX, contactProps); + final String givenName = loadStringProperty(SP_CONTACT_GIVEN_NAME_PROPERTY_KEY_SUFFIX, contactProps); + final String surName = loadStringProperty(SP_CONTACT_SUR_NAME_PROPERTY_KEY_SUFFIX, contactProps); + // split properties into a map of properties + // key = e-mail address index; value = e-mail address + final SortedMap emailAddresses = extractIndexedValues(SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX, contactProps); + final List emails = toStringList(emailAddresses); + // split properties into a map of properties + // key = phone number index; value = phone numbers + final SortedMap phoneNumbers = extractIndexedValues(SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX, contactProps); + final List numbers = toStringList(phoneNumbers); + return new Contact(contactType, company, givenName, surName, emails, numbers); + } + + /** + * Given a map containing settings data, extracts all the indexed properties + * identified by a given prefix. The returned map has indexes as keys and a map + * describing the extracted indexed data as values. Keys are sorted by their + * natural order (i.e. iterating over the map will return entries in index order). + *

+ * For instance, if the prefix is foo, all the following properties + * will be extracted: + * + *

+	 * foo[0].prop1=<value1>
+	 * foo[0].prop2=<value2>
+	 * foo[1].prop1=<value3>
+	 * 
+ * + * and the returned map will be: + * + *
+	 * 0 => prop1=<value1>
+	 *      prop2=<value2>
+	 * 1 => prop1=<value3>
+	 * 
+ * + * The index is optional: if missing, "-1" is returned. In other words, in the + * above example: + * + *
+	 * foo.prop1=<value1>
+	 * foo.prop2=<value2>
+	 * 
+ * + * will be mapped to: + * + *
+	 * -1 => prop1=<value1>
+	 *       prop2=<value2>
+	 * 
+ * + * Indices can be made of maximum 9 digits, to prevent overflows. Leading zeroes + * are discarded. + * + * @param prefix + * the prefix that identifies the indexed property to extract + * @param data + * the input data + * @return a map with extracted data for each identified index + */ + private SortedMap> extractIndexedProperties(String prefix, Map data) { + final Pattern p = Pattern.compile(Pattern.quote(prefix) + + "(?:\\[(\\d{1,9})\\])?\\.(.+)"); + final SortedMap> indexedProps = new TreeMap<>(); + for(final Entry prop: data.entrySet()) { + final Matcher m = p.matcher(prop.getKey()); + if(m.matches()) { + final String indexString = m.group(1); + final int index = indexString == null? -1: Integer.parseInt(indexString); + final String suffix = m.group(2); + Map props = indexedProps.get(index); + if(props == null) { + props = new HashMap<>(); + indexedProps.put(index, props); + } + props.put(suffix, prop.getValue()); + } + } + return indexedProps; + } + + /** + * Given a map containing settings data, extracts all the indexed values + * identified by a given prefix. The returned map has indexes as keys and the + * corresponding values as values. Keys are sorted by their natural order + * (i.e. iterating over the map will return entries in index order). + *

+ * For instance, if the prefix is foo, all the following values + * will be extracted: + * + *

+	 * foo[0]=<value1>
+	 * foo[1]=<value2>
+	 * foo[2]=<value3>
+	 * 
+ * + * and the returned map will be: + * + *
+	 * 0 => <value1>
+	 * 1 => <value2>
+	 * 3 => <value3>
+	 * 
+ * + * The index is optional: if missing, "-1" is returned. In other words, in the + * above example: + * + *
+	 * foo=<value1>
+	 * 
+ * + * will be mapped to: + * + *
+	 * -1 => <value1>
+	 * 
+ * + * Indices can be made of maximum 9 digits, to prevent overflows. Leading zeroes + * are discarded. + * + * @param prefix + * the prefix that identifies the indexed property to extract + * @param data + * the input data + * @return a map with extracted values for each identified index + */ + private SortedMap extractIndexedValues(String prefix, Map data) { + final Pattern p = Pattern.compile(Pattern.quote(prefix) + + "(?:\\[(\\d{1,9})\\])?"); + final SortedMap indexedValues = new TreeMap<>(); + for(final Entry prop: data.entrySet()) { + final Matcher m = p.matcher(prop.getKey()); + if(m.matches()) { + final String indexString = m.group(1); + final int index = indexString == null? -1: Integer.parseInt(indexString); + indexedValues.put(index, prop.getValue()); + } + } + return indexedValues; + } + + /** + * Given a map of indexed property values (possibly extracted with + * {@link #extractIndexedValues(String, Map)}), returns a list containing all + * the {@link String} values contained in the map, sorted by their iteration + * order. + * + * @param indexedValues + * a map containing indexed values (key = index; value = actual + * value) + * @return a list containing all the string values in the input map, sorted by + * their iteration order; therefore, if the map is a {@link SortedMap}, + * the returned list has values sorted by their index + */ + private List toStringList(final Map indexedValues) { + return indexedValues.values().stream() + .map(value -> isString(value) ? StringUtils.trimToNull((String) value) : null) + .filter(Objects::nonNull).collect(Collectors.toList()); + } + /** * Loads the unique ID prefix. Uses default if property not set. */ @@ -568,13 +772,25 @@ private void loadSpSetting() { * @return the value */ private String loadStringProperty(String propertyKey) { - Object propValue = samlData.get(propertyKey); + return loadStringProperty(propertyKey, samlData); + } + + /** + * Loads a property of the type String from the specified data + * + * @param propertyKey the property name + * @param data the input data + * + * @return the value + */ + private String loadStringProperty(String propertyKey, Map data) { + Object propValue = data.get(propertyKey); if (isString(propValue)) { return StringUtils.trimToNull((String) propValue); } return null; } - + /** * Loads a property of the type Boolean from the Properties object * diff --git a/core/src/main/java/com/onelogin/saml2/util/Constants.java b/core/src/main/java/com/onelogin/saml2/util/Constants.java index dd3037a7..d3aae65b 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Constants.java +++ b/core/src/main/java/com/onelogin/saml2/util/Constants.java @@ -83,6 +83,13 @@ public final class Constants { public static final String STATUS_UNKNOWN_PRINCIPAL = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"; public static final String STATUS_UNSUPPORTED_BINDING = "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding"; + // Contact types + public static final String CONTACT_TYPE_TECHNICAL = "technical"; + public static final String CONTACT_TYPE_SUPPORT = "support"; + public static final String CONTACT_TYPE_ADMINISTRATIVE = "administrative"; + public static final String CONTACT_TYPE_BILLING = "billing"; + public static final String CONTACT_TYPE_OTHER = "other"; + // Canonization public static final String C14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; public static final String C14N_WC = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; @@ -91,7 +98,7 @@ public final class Constants { public static final String C14NEXC = "http://www.w3.org/2001/10/xml-exc-c14n#"; public static final String C14NEXC_WC = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; - // Sign & Crypt + // Sign & Crypt // https://www.w3.org/TR/xmlenc-core/#sec-Alg-MessageDigest // https://www.w3.org/TR/xmlsec-algorithms/#signature-method-uris // https://tools.ietf.org/html/rfc6931 diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index 7e45aace..c3c25e44 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -236,9 +236,12 @@ public void testInjectIntoSettings() throws Exception { assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); - assertEquals(2, setting.getContacts().size()); - assertEquals("technical@example.com", setting.getContacts().get(0).getEmailAddress()); - assertEquals("support@example.com", setting.getContacts().get(1).getEmailAddress()); + assertEquals(4, setting.getContacts().size()); + assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); + assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); + assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); + assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); + assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); assertEquals("SP Java", setting.getOrganization().getOrgName()); assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); @@ -257,9 +260,12 @@ public void testInjectIntoSettings() throws Exception { assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); - assertEquals(2, setting.getContacts().size()); - assertEquals("technical@example.com", setting.getContacts().get(0).getEmailAddress()); - assertEquals("support@example.com", setting.getContacts().get(1).getEmailAddress()); + assertEquals(4, setting.getContacts().size()); + assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); + assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); + assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); + assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); + assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); assertEquals("SP Java", setting.getOrganization().getOrgName()); assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java index f94e6f95..df5e3328 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java @@ -147,21 +147,24 @@ public void testToContactsXml() throws IOException, CertificateEncodingException Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); - String contactStr = "Technical Guytechnical@example.comSupport Guysupport@example.com"; - assertThat(metadataStr, containsString(contactStr)); + String administrativeContactStr = "ACMEGuyAdministrativeadministrative@example.comadministrative2@example.com+1-123456789+1-987654321"; + assertThat(metadataStr, containsString(administrativeContactStr)); + String otherContactStr = "Big Corpinfo@example.com"; + assertThat(metadataStr, containsString(otherContactStr)); Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); Metadata metadataObj2 = new Metadata(settings2); String metadataStr2 = metadataObj2.getMetadataString(); - assertThat(metadataStr2, not(containsString(contactStr))); + assertThat(metadataStr2, not(containsString(administrativeContactStr))); + assertThat(metadataStr2, not(containsString(otherContactStr))); } /** * Tests the toContactsXml method of Metadata *

* Case: contacts text containing special chars. - * + * * @throws IOException * @throws CertificateEncodingException * @throws Error @@ -173,8 +176,66 @@ public void testToContactsXmlSpecialChars() throws IOException, CertificateEncod Metadata metadataObj = new Metadata(settings); String metadataStr = metadataObj.getMetadataString(); - String contactStr = "T&chnical Guyt&chnical@example.com"Support Guy"supp&rt@example.com"; - assertThat(metadataStr, containsString(contactStr)); + String administrativeContactStr = "ACME & C."Guy"<Administrative>administrativ&@example.comadministrativ&2@example.com<+1>-123456789<+1>-987654321"; + assertThat(metadataStr, containsString(administrativeContactStr)); + String otherContactStr = "Big Corpinfo@example.com"; + assertThat(metadataStr, containsString(otherContactStr)); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Metadata metadataObj2 = new Metadata(settings2); + String metadataStr2 = metadataObj2.getMetadataString(); + + assertThat(metadataStr2, not(containsString(administrativeContactStr))); + assertThat(metadataStr2, not(containsString(otherContactStr))); + } + + /** + * Tests the toContactsXml method of Metadata with regards to legacy contacts definition + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toContactsXml + */ + @Test + public void testToContactsXmlLegacy() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllProperties(); + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + + String technicalContactStr = "Technical Guytechnical@example.com"; + assertThat(metadataStr, containsString(technicalContactStr)); + String supportContactStr = "Support Guysupport@example.com"; + assertThat(metadataStr, containsString(supportContactStr)); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Metadata metadataObj2 = new Metadata(settings2); + String metadataStr2 = metadataObj2.getMetadataString(); + + assertThat(metadataStr2, not(containsString(technicalContactStr))); + assertThat(metadataStr2, not(containsString(supportContactStr))); + } + + /** + * Tests the toContactsXml method of Metadata with regards to legacy contact definition + *

+ * Case: contacts text containing special chars. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toContactsXml + */ + @Test + public void testToContactsXmlLegacySpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllSpecialCharsProperties(); + Metadata metadataObj = new Metadata(settings); + String metadataStr = metadataObj.getMetadataString(); + + String technicalContactStr = "T&chnical Guyt&chnical@example.com"; + assertThat(metadataStr, containsString(technicalContactStr)); + String supportContactStr = ""Support Guy"supp&rt@example.com"; + assertThat(metadataStr, containsString(supportContactStr)); } /** diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java index f9e31dde..b9206d4f 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/SettingBuilderTest.java @@ -291,15 +291,47 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertTrue(org.equalsTo(setting.getOrganization())); List contacts = setting.getContacts(); - assertEquals(2, contacts.size()); + assertEquals(4, contacts.size()); Contact c1 = contacts.get(0); - assertEquals("technical", c1.getContactType()); - assertEquals("technical@example.com", c1.getEmailAddress()); - assertEquals("Technical Guy", c1.getGivenName()); + assertEquals("administrative", c1.getContactType()); + assertEquals("ACME", c1.getCompany()); + assertEquals("Guy", c1.getGivenName()); + assertEquals("Administrative", c1.getSurName()); + assertEquals(2, c1.getEmailAddresses().size()); + assertEquals("administrative@example.com", c1.getEmailAddresses().get(0)); + assertEquals("administrative2@example.com", c1.getEmailAddresses().get(1)); + List c1Phones = c1.getTelephoneNumbers(); + assertEquals(2, c1Phones.size()); + assertEquals("+1-123456789", c1Phones.get(0)); + assertEquals("+1-987654321", c1Phones.get(1)); Contact c2 = contacts.get(1); - assertEquals("support", c2.getContactType()); - assertEquals("support@example.com", c2.getEmailAddress()); - assertEquals("Support Guy", c2.getGivenName()); + assertEquals("other", c2.getContactType()); + assertEquals("Big Corp", c2.getCompany()); + assertNull(c2.getGivenName()); + assertNull(c2.getSurName()); + assertEquals(1, c2.getEmailAddresses().size()); + assertEquals("info@example.com", c2.getEmailAddresses().get(0)); + assertEquals(0, c2.getTelephoneNumbers().size()); + Contact c3 = contacts.get(2); + assertEquals("technical", c3.getContactType()); + assertNull(c3.getCompany()); + assertEquals("Technical Guy", c3.getGivenName()); + assertNull(c3.getSurName()); + List c3Emails = c3.getEmailAddresses(); + assertEquals(1, c3Emails.size()); + assertEquals("technical@example.com", c3Emails.get(0)); + assertEquals("technical@example.com", c3.getEmailAddress()); + assertEquals(0, c3.getTelephoneNumbers().size()); + Contact c4 = contacts.get(3); + assertEquals("support", c4.getContactType()); + assertNull(c4.getCompany()); + assertEquals("Support Guy", c4.getGivenName()); + assertNull(c4.getSurName()); + List c4Emails = c4.getEmailAddresses(); + assertEquals(1, c4Emails.size()); + assertEquals("support@example.com", c4Emails.get(0)); + assertEquals("support@example.com", c4.getEmailAddress()); + assertEquals(0, c4.getTelephoneNumbers().size()); assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); } @@ -753,6 +785,19 @@ public void testLoadFromValues() throws Exception { samlData.put(ORGANIZATION_LANG, "en"); // Contacts + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX, "administrative"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX, "ACME"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_GIVEN_NAME_PROPERTY_KEY_SUFFIX, "Guy"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_SUR_NAME_PROPERTY_KEY_SUFFIX, "Administrative"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX + "[0]", "administrative@example.com"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX + "[1]", "administrative2@example.com"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX + "[0]", "+1-123456789"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX + "[1]", "+1-987654321"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX, "other"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX, "Big Corp"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX, "info@example.com"); + + // Legacy contacts samlData.put(CONTACT_TECHNICAL_GIVEN_NAME, "Technical Guy"); samlData.put(CONTACT_TECHNICAL_EMAIL_ADDRESS, "technical@example.org"); samlData.put(CONTACT_SUPPORT_GIVEN_NAME, "Support Guy"); @@ -819,15 +864,48 @@ public void testLoadFromValues() throws Exception { assertTrue(org.equalsTo(setting.getOrganization())); List contacts = setting.getContacts(); - assertEquals(2, contacts.size()); + assertEquals(4, contacts.size()); Contact c1 = contacts.get(0); - assertEquals("technical", c1.getContactType()); - assertEquals("technical@example.org", c1.getEmailAddress()); - assertEquals("Technical Guy", c1.getGivenName()); + assertEquals("administrative", c1.getContactType()); + assertEquals("ACME", c1.getCompany()); + assertEquals("Guy", c1.getGivenName()); + assertEquals("Administrative", c1.getSurName()); + List c1Emails = c1.getEmailAddresses(); + assertEquals(2, c1Emails.size()); + assertEquals("administrative@example.com", c1Emails.get(0)); + assertEquals("administrative2@example.com", c1Emails.get(1)); + List c1Phones = c1.getTelephoneNumbers(); + assertEquals(2, c1Phones.size()); + assertEquals("+1-123456789", c1Phones.get(0)); + assertEquals("+1-987654321", c1Phones.get(1)); Contact c2 = contacts.get(1); - assertEquals("support", c2.getContactType()); - assertEquals("support@example.org", c2.getEmailAddress()); - assertEquals("Support Guy", c2.getGivenName()); + assertEquals("other", c2.getContactType()); + assertEquals("Big Corp", c2.getCompany()); + assertNull(c2.getGivenName()); + assertNull(c2.getSurName()); + List c2Emails = c2.getEmailAddresses(); + assertEquals(1, c2Emails.size()); + assertTrue(c2.getTelephoneNumbers().isEmpty()); + Contact c3 = contacts.get(2); + assertEquals("technical", c3.getContactType()); + assertNull(c3.getCompany()); + assertEquals("Technical Guy", c3.getGivenName()); + assertNull(c3.getSurName()); + List c3Emails = c3.getEmailAddresses(); + assertEquals(1, c3Emails.size()); + assertEquals("technical@example.org", c3Emails.get(0)); + assertEquals("technical@example.org", c3.getEmailAddress()); + assertTrue(c3.getTelephoneNumbers().isEmpty()); + Contact c4 = contacts.get(3); + assertEquals("support", c4.getContactType()); + assertNull(c4.getCompany()); + assertEquals("Support Guy", c4.getGivenName()); + assertNull(c4.getSurName()); + List c4Emails = c4.getEmailAddresses(); + assertEquals(1, c4Emails.size()); + assertEquals("support@example.org", c4Emails.get(0)); + assertEquals("support@example.org", c4.getEmailAddress()); + assertTrue(c4.getTelephoneNumbers().isEmpty()); assertEquals("_", setting.getUniqueIDPrefix()); @@ -917,6 +995,19 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(ORGANIZATION_LANG, "en"); // Contacts + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX, "administrative"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX, "ACME"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_GIVEN_NAME_PROPERTY_KEY_SUFFIX, "Guy"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_SUR_NAME_PROPERTY_KEY_SUFFIX, "Administrative"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX + "[0]", "administrative@example.com"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX + "[1]", "administrative2@example.com"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX + "[0]", "+1-123456789"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[0]." + SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX + "[1]", "+1-987654321"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_CONTACT_TYPE_PROPERTY_KEY_SUFFIX, "other"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_COMPANY_PROPERTY_KEY_SUFFIX, "Big Corp"); + samlData.put(SP_CONTACT_PROPERTY_KEY_PREFIX + "[1]." + SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX, "info@example.com"); + + // Legacy contacts samlData.put(CONTACT_TECHNICAL_GIVEN_NAME, "Technical Guy"); samlData.put(CONTACT_TECHNICAL_EMAIL_ADDRESS, "technical@example.org"); samlData.put(CONTACT_SUPPORT_GIVEN_NAME, "Support Guy"); @@ -975,15 +1066,48 @@ public void testLoadFromValuesWithObjects() throws Exception { assertTrue(org.equalsTo(setting.getOrganization())); List contacts = setting.getContacts(); - assertEquals(2, contacts.size()); + assertEquals(4, contacts.size()); Contact c1 = contacts.get(0); - assertEquals("technical", c1.getContactType()); - assertEquals("technical@example.org", c1.getEmailAddress()); - assertEquals("Technical Guy", c1.getGivenName()); + assertEquals("administrative", c1.getContactType()); + assertEquals("ACME", c1.getCompany()); + assertEquals("Guy", c1.getGivenName()); + assertEquals("Administrative", c1.getSurName()); + List c1Emails = c1.getEmailAddresses(); + assertEquals(2, c1Emails.size()); + assertEquals("administrative@example.com", c1Emails.get(0)); + assertEquals("administrative2@example.com", c1Emails.get(1)); + List c1Phones = c1.getTelephoneNumbers(); + assertEquals(2, c1Phones.size()); + assertEquals("+1-123456789", c1Phones.get(0)); + assertEquals("+1-987654321", c1Phones.get(1)); Contact c2 = contacts.get(1); - assertEquals("support", c2.getContactType()); - assertEquals("support@example.org", c2.getEmailAddress()); - assertEquals("Support Guy", c2.getGivenName()); + assertEquals("other", c2.getContactType()); + assertEquals("Big Corp", c2.getCompany()); + assertNull(c2.getGivenName()); + assertNull(c2.getSurName()); + List c2Emails = c2.getEmailAddresses(); + assertEquals(1, c2Emails.size()); + assertTrue(c2.getTelephoneNumbers().isEmpty()); + Contact c3 = contacts.get(2); + assertEquals("technical", c3.getContactType()); + assertNull(c3.getCompany()); + assertEquals("Technical Guy", c3.getGivenName()); + assertNull(c3.getSurName()); + List c3Emails = c3.getEmailAddresses(); + assertEquals(1, c3Emails.size()); + assertEquals("technical@example.org", c3Emails.get(0)); + assertEquals("technical@example.org", c3.getEmailAddress()); + assertTrue(c3.getTelephoneNumbers().isEmpty()); + Contact c4 = contacts.get(3); + assertEquals("support", c4.getContactType()); + assertNull(c4.getCompany()); + assertEquals("Support Guy", c4.getGivenName()); + assertNull(c4.getSurName()); + List c4Emails = c4.getEmailAddresses(); + assertEquals(1, c4Emails.size()); + assertEquals("support@example.org", c4Emails.get(0)); + assertEquals("support@example.org", c4.getEmailAddress()); + assertTrue(c4.getTelephoneNumbers().isEmpty()); assertEquals("ONELOGIN_", setting.getUniqueIDPrefix()); } diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index b2741928..fcdbf242 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -153,6 +153,19 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.organization.lang = en # Contacts +onelogin.saml2.sp.contact[0].contactType=administrative +onelogin.saml2.sp.contact[0].company=ACME +onelogin.saml2.sp.contact[0].given_name=Guy +onelogin.saml2.sp.contact[0].sur_name=Administrative +onelogin.saml2.sp.contact[0].email_address[0]=administrative@example.com +onelogin.saml2.sp.contact[0].email_address[1]=administrative2@example.com +onelogin.saml2.sp.contact[0].telephone_number[0]=+1-123456789 +onelogin.saml2.sp.contact[0].telephone_number[1]=+1-987654321 +onelogin.saml2.sp.contact[1].contactType=other +onelogin.saml2.sp.contact[1].company=Big Corp +onelogin.saml2.sp.contact[1].email_address=info@example.com + +# Legacy contacts onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy diff --git a/core/src/test/resources/config/config.all_specialchars.properties b/core/src/test/resources/config/config.all_specialchars.properties index a4492871..923297e0 100644 --- a/core/src/test/resources/config/config.all_specialchars.properties +++ b/core/src/test/resources/config/config.all_specialchars.properties @@ -144,6 +144,19 @@ onelogin.saml2.organization.url = http://sp.example.com?a=1&b=2 onelogin.saml2.organization.lang = en # Contacts +onelogin.saml2.sp.contact[0].contactType=administrative +onelogin.saml2.sp.contact[0].company=ACME & C. +onelogin.saml2.sp.contact[0].given_name="Guy" +onelogin.saml2.sp.contact[0].sur_name= +onelogin.saml2.sp.contact[0].email_address[0]=administrativ&@example.com +onelogin.saml2.sp.contact[0].email_address[1]=administrativ&2@example.com +onelogin.saml2.sp.contact[0].telephone_number[0]=<+1>-123456789 +onelogin.saml2.sp.contact[0].telephone_number[1]=<+1>-987654321 +onelogin.saml2.sp.contact[1].contactType=other +onelogin.saml2.sp.contact[1].company=Big Corp +onelogin.saml2.sp.contact[1].email_address=info@example.com + +# Legacy contacts onelogin.saml2.contacts.technical.given_name = T&chnical Guy onelogin.saml2.contacts.technical.email_address = t&chnical@example.com onelogin.saml2.contacts.support.given_name = "Support Guy" From 7735163e63226db3aaab717c4456b3d75d6dad70 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Fri, 5 Nov 2021 18:12:35 +0100 Subject: [PATCH 109/133] Update microsoft identity/keyvalut dependency --- core/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index a00edff3..1a23fc65 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,13 +72,13 @@ com.azure azure-security-keyvault-keys - 4.3.3 + 4.3.4 true com.azure azure-identity - 1.3.6 + 1.3.7 true @@ -118,7 +118,7 @@ maven-surefire-plugin 2.22.2 - ${project.build.sourceEncoding} + ${project.build.sourceEncoding} ${project.build.sourceEncoding} ${project.build.sourceEncoding} ${jacoco.agent.argLine} -Dfile.encoding=${project.build.sourceEncoding} -Dline.separator=\n From 6900702b216646f0fda46040e200b38c626fa942 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 5 Nov 2021 18:39:17 +0100 Subject: [PATCH 110/133] Revise SP contacts settings validation The contact type check now becomes useful (and so it was restored), because with the new full Contacts support the user may indeed specify invalid contact types in settings. The "not enough data" check, instead, was fixed and it is now raised only if ALL of the contact data (company, given name, surname, e-mail addresses and phone names) are empty, reflecting the actual SAML 2.0 metadata schema constraint. Fixes #353. --- .../saml2/settings/Saml2Settings.java | 30 +++++++++---------- .../test/settings/Saml2SettingsTest.java | 2 ++ .../config/config.allerrors.properties | 4 ++- .../config/config.sperrors.properties | 4 ++- .../com/onelogin/saml2/test/AuthTest.java | 2 +- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index 292b75e6..9ee29465 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -5,8 +5,10 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import com.onelogin.saml2.model.hsm.HSM; @@ -1017,27 +1019,25 @@ public List checkSPSettings() { List contacts = this.getContacts(); if (!contacts.isEmpty()) { -/* - List validTypes = new ArrayList(); - validTypes.add("technical"); - validTypes.add("support"); - validTypes.add("administrative"); - validTypes.add("billing"); - validTypes.add("other"); -*/ + Set validTypes = new HashSet<>(); + validTypes.add(Constants.CONTACT_TYPE_TECHNICAL); + validTypes.add(Constants.CONTACT_TYPE_SUPPORT); + validTypes.add(Constants.CONTACT_TYPE_ADMINISTRATIVE); + validTypes.add(Constants.CONTACT_TYPE_BILLING); + validTypes.add(Constants.CONTACT_TYPE_OTHER); for (Contact contact : contacts) { -/* if (!validTypes.contains(contact.getContactType())) { errorMsg = "contact_type_invalid"; errors.add(errorMsg); LOGGER.error(errorMsg); } -*/ - - if (contact.getEmailAddresses().isEmpty() || contact.getEmailAddresses().stream().allMatch(StringUtils::isEmpty) || - (StringUtils.isEmpty(contact.getCompany()) && - StringUtils.isEmpty(contact.getGivenName()) && - StringUtils.isEmpty(contact.getSurName()))) { + if ((contact.getEmailAddresses().isEmpty() + || contact.getEmailAddresses().stream().allMatch(StringUtils::isEmpty)) + && (contact.getTelephoneNumbers().isEmpty() || contact.getTelephoneNumbers() + .stream().allMatch(StringUtils::isEmpty)) + && StringUtils.isEmpty(contact.getCompany()) + && StringUtils.isEmpty(contact.getGivenName()) + && StringUtils.isEmpty(contact.getSurName())) { errorMsg = "contact_not_enough_data"; errors.add(errorMsg); LOGGER.error(errorMsg); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java index 8a3875da..67bbf84b 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/Saml2SettingsTest.java @@ -114,6 +114,7 @@ public void testCheckSPSettingsAllErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("sp_entityId_not_found")); assertThat(settingsErrors, hasItem("sp_acs_not_found")); assertThat(settingsErrors, hasItem("sp_cert_not_found_and_required")); + assertThat(settingsErrors, hasItem("contact_type_invalid")); assertThat(settingsErrors, hasItem("contact_not_enough_data")); assertThat(settingsErrors, hasItem("organization_not_enough_data")); } @@ -151,6 +152,7 @@ public void testCheckSettingsAllErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("sp_entityId_not_found")); assertThat(settingsErrors, hasItem("sp_acs_not_found")); assertThat(settingsErrors, hasItem("sp_cert_not_found_and_required")); + assertThat(settingsErrors, hasItem("contact_type_invalid")); assertThat(settingsErrors, hasItem("contact_not_enough_data")); assertThat(settingsErrors, hasItem("organization_not_enough_data")); assertThat(settingsErrors, hasItem("idp_entityId_not_found")); diff --git a/core/src/test/resources/config/config.allerrors.properties b/core/src/test/resources/config/config.allerrors.properties index 1b41f6f0..80f5e1a4 100644 --- a/core/src/test/resources/config/config.allerrors.properties +++ b/core/src/test/resources/config/config.allerrors.properties @@ -19,4 +19,6 @@ onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.url = http://sp.example.com # Contacts -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.sp.contact[0].contactType=administrative +onelogin.saml2.sp.contact[1].contactType=nonexistent +onelogin.saml2.sp.contact[1].company=ACME \ No newline at end of file diff --git a/core/src/test/resources/config/config.sperrors.properties b/core/src/test/resources/config/config.sperrors.properties index 6a1a649f..f80b95d1 100644 --- a/core/src/test/resources/config/config.sperrors.properties +++ b/core/src/test/resources/config/config.sperrors.properties @@ -30,4 +30,6 @@ onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example # Contacts -onelogin.saml2.contacts.support.given_name = Support Guy \ No newline at end of file +onelogin.saml2.sp.contact[0].contactType=administrative +onelogin.saml2.sp.contact[1].contactType=nonexistent +onelogin.saml2.sp.contact[1].company=ACME \ No newline at end of file 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 6c376839..cd45125f 100644 --- a/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java +++ b/toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java @@ -303,7 +303,7 @@ public void testConstructorInvalidSettings() throws IOException, SettingsExcepti Saml2Settings settings = new SettingsBuilder().fromFile("config/config.sperrors.properties").build(); expectedEx.expect(SettingsException.class); - expectedEx.expectMessage("Invalid settings: sp_entityId_not_found, sp_acs_not_found, sp_cert_not_found_and_required, contact_not_enough_data, organization_not_enough_data, idp_cert_or_fingerprint_not_found_and_required, idp_cert_not_found_and_required"); + expectedEx.expectMessage("Invalid settings: sp_entityId_not_found, sp_acs_not_found, sp_cert_not_found_and_required, contact_not_enough_data, contact_type_invalid, organization_not_enough_data, idp_cert_or_fingerprint_not_found_and_required, idp_cert_not_found_and_required"); new Auth(settings, request, response); } From c02eb9fe55888aa10e3ef741dc2756d3e66d3ab2 Mon Sep 17 00:00:00 2001 From: kemalturksonmez <56357795+kemalturksonmez@users.noreply.github.com> Date: Wed, 24 Nov 2021 09:29:25 -0500 Subject: [PATCH 111/133] Update parseXML to use XMLErrorAccumulatorHandler Prevent xerces libraries from outputting to stderr and give users the ability to control logging. --- core/src/main/java/com/onelogin/saml2/util/Util.java | 2 ++ 1 file changed, 2 insertions(+) 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 086e03f9..8a6be158 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -394,6 +394,8 @@ public static Document parseXML(InputSource inputSource) throws ParserConfigurat } catch (Throwable e) {} DocumentBuilder builder = docfactory.newDocumentBuilder(); + XMLErrorAccumulatorHandler errorAcumulator = new XMLErrorAccumulatorHandler(); + builder.setErrorHandler(errorAcumulator); Document doc = builder.parse(inputSource); // Loop through the doc and tag every element with an ID attribute From f58823780feff1ca6e7d758be9e6af616735e677 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 26 Jan 2022 12:19:00 -0500 Subject: [PATCH 112/133] docs: update to use 2.8.0 Signed-off-by: Rui Chen --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index df0c3bf0..d8064572 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,17 @@ Version 2.0.0 - 2.4.0, compatible with java7 / java8. We [introduced some incompatibilities](https://github.com/onelogin/java-saml/issues/90), that could be fixed and make it compatible with java6. -Version 1.1.2 is considered to be deprecated. If you have used it, we strongly recommend that you migrate to the new version. +Version 1.1.2 is considered to be deprecated. If you have used it, we strongly recommend that you migrate to the new version. We rebuilt the toolkit on 2.0.0, so code/settings that you had been using in the previous version will no longer be compatible. ## Why add SAML support to my software? SAML is an XML-based standard for web browser single sign-on and is defined by -the OASIS Security Services Technical Committee. The standard has been around +the OASIS Security Services Technical Committee. The standard has been around since 2002, but lately it has become popular due to its advantages as follows: - * **Usability** - One-click access from portals or intranets, deep linking, + * **Usability** - One-click access from portals or intranets, deep linking, password elimination and automatically renewing sessions make life easier for the user. * **Security** - Based on strong digital signatures for authentication and @@ -40,7 +40,7 @@ since 2002, but lately it has become popular due to its advantages as follows: * **IT Friendly** - SAML simplifies life for IT because it centralizes authentication, provides greater visibility and makes directory integration easier. - * **Opportunity** - B2B cloud vendor should support SAML to facilitate the + * **Opportunity** - B2B cloud vendor should support SAML to facilitate the integration of their product. @@ -75,7 +75,7 @@ In production, the **onelogin.saml2.strict** setting parameter MUST be set as ** In production also we highly recommend to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment. -The IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. +The IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed. Usually the same administrator that handles the Service Provider also sets the URL to the IdP, which should be a trusted resource. @@ -97,7 +97,7 @@ Install it as a maven dependency: com.onelogin java-saml - 2.6.0 + 2.8.0 ``` @@ -246,12 +246,12 @@ onelogin.saml2.sp.x509cert = # Future SP certificate, to be used during SP Key roll over onelogin.saml2.sp.x509certNew = -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.organization.lang = en @@ -269,7 +269,7 @@ onelogin.saml2.sp.contact[1].contactType=other onelogin.saml2.sp.contact[1].company=Big Corp onelogin.saml2.sp.contact[1].email_address=info@example.com -# Legacy contacts (legacy way to specify just a technical and a support contact with minimal info) +# Legacy contacts (legacy way to specify just a technical and a support contact with minimal info) onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy @@ -284,7 +284,7 @@ onelogin.saml2.idp.entityid = # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = -# SAML protocol binding to be used to deliver the message +# SAML protocol binding to be used to deliver the message # to the IdP. Onelogin Toolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -316,7 +316,7 @@ onelogin.saml2.idp.x509cert = # If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to # let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512 # 'sha1' is the default value. -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha256 # Security settings @@ -346,7 +346,7 @@ onelogin.saml2.security.want_messages_signed = false onelogin.saml2.security.want_assertions_signed = false # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null (in order to not sign) or true (sign using SP private key) +# Right now supported null (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -394,7 +394,7 @@ onelogin.saml2.security.reject_deprecated_alg = true # SAML specification states that no trimming for string elements should be performed, so no trimming will be # performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add # undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). -# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and # attribute values. onelogin.saml2.parsing.trim_name_ids = false onelogin.saml2.parsing.trim_attribute_values = false @@ -665,7 +665,7 @@ and later executing the redirection manually. ### Extending the provided implementation -All the provided SAML message classes (`AuthnRequest`, `SamlResponse`, `LogoutRequest`, `LogoutResponse`) can be extended to add or change the processing behavior. +All the provided SAML message classes (`AuthnRequest`, `SamlResponse`, `LogoutRequest`, `LogoutResponse`) can be extended to add or change the processing behavior. In particular, the classes used to produce outgoing messages (`AuthnRequest`, `LogoutRequest`, and `LogoutResponse`) also provide a `postProcessXml` method that can be overridden to customise the generation of the corresponding SAML message XML, along with the ability to pass in proper extensions of the input parameter classes (`AuthnRequestParams`, `LogoutRequestParams`, and `LogoutResponseParams` respectively). @@ -683,7 +683,7 @@ auth.setSamlMessageFactory(new SamlMessageFactory() { public SamlResponse createSamlResponse(Saml2Settings settings, HttpRequest request) throws Exception { return new SamlResponseEx(settings, request); } -}); +}); // then proceed with login... auth.login(relayState, new AuthnRequestParamsEx()); // the custom generation of AuthnReqeustEx will be executed // ... or process the response as usual @@ -700,12 +700,12 @@ For Apache Tomcat this is done by setting the proxyName, proxyPort, scheme and s ### IdP with multiple certificates - + In some scenarios the IdP uses different certificates for signing/encryption, or is under key rollover phase and more than one certificate is published on IdP metadata. - + In order to handle that the toolkit offers the `onelogin.saml2.idp.x509certMulti` parameters where you can set additional certificates that will be used to validate IdP signature. However just the certificate set in `onelogin.saml2.idp.x509cert` parameter will be used for encrypting. - + ### Replay attacks From bf7671a586f74b23eadb5a59399d63289cc0c56e Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 26 Jan 2022 12:19:22 -0500 Subject: [PATCH 113/133] docs: add toc Signed-off-by: Rui Chen --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d8064572..ffee6d09 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OneLogin's SAML Java Toolkit +# OneLogin's SAML Java Toolkit [![Build Status](https://travis-ci.org/onelogin/java-saml.svg?branch=master)](https://travis-ci.org/onelogin/java-saml) [![Coverage Status](https://coveralls.io/repos/github/onelogin/java-saml/badge.svg?branch=master)](https://coveralls.io/github/onelogin/java-saml?branch=master) @@ -20,6 +20,45 @@ We [introduced some incompatibilities](https://github.com/onelogin/java-saml/iss Version 1.1.2 is considered to be deprecated. If you have used it, we strongly recommend that you migrate to the new version. We rebuilt the toolkit on 2.0.0, so code/settings that you had been using in the previous version will no longer be compatible. +- [Why add SAML support to my software?](#why-add-saml-support-to-my-software) +- [General description](#general-description) +- [Security warning](#security-warning) +- [Installation](#installation) + - [Hosting](#hosting) + - [Github](#github) + - [Maven](#maven) + - [Dependencies](#dependencies) +- [Working with the github repository code and Eclipse.](#working-with-the-github-repository-code-and-eclipse) + - [Get the toolkit.](#get-the-toolkit) + - [Adding java-saml toolkit components as a project](#adding-java-saml-toolkit-components-as-a-project) + - [Adding the java-saml-tookit-jspsample as a project](#adding-the-java-saml-tookit-jspsample-as-a-project) + - [Deploy the java-saml-tookit-jspsample](#deploy-the-java-saml-tookit-jspsample) +- [Getting started](#getting-started) + - [Learning the toolkit](#learning-the-toolkit) + - [core (com.onelogin:java-saml-core)](#core-comoneloginjava-saml-core) + - [toolkit (com.onelogin:java-saml)](#toolkit-comoneloginjava-saml) + - [samples (com.onelogin:java-saml-tookit-samples)](#samples-comoneloginjava-saml-tookit-samples) + - [How it works](#how-it-works) + - [Javadocs](#javadocs) + - [Settings](#settings) + - [Properties File](#properties-file) + - [KeyStores](#keystores) + - [Dynamic Settings](#dynamic-settings) + - [The HttpRequest](#the-httprequest) + - [Initiate SSO](#initiate-sso) + - [The SP Endpoints](#the-sp-endpoints) + - [SP Metadata](#sp-metadata) + - [Attribute Consumer Service(ACS)](#attribute-consumer-serviceacs) + - [Single Logout Service (SLS)](#single-logout-service-sls) + - [Initiate SLO](#initiate-slo) + - [Extending the provided implementation](#extending-the-provided-implementation) + - [Working behind load balancer](#working-behind-load-balancer) + - [IdP with multiple certificates](#idp-with-multiple-certificates) + - [Replay attacks](#replay-attacks) +- [Demo included in the toolkit](#demo-included-in-the-toolkit) + - [SP setup](#sp-setup) + - [IdP setup](#idp-setup) + - [How it works](#how-it-works-1) ## Why add SAML support to my software? From 4f4340cabe68bbdfd93d80f869b6ed200cd9afae Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 1 Feb 2022 17:34:45 +0100 Subject: [PATCH 114/133] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f18ede37..beeef0c0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,9 +12,9 @@ UTF-8 UTF-8 - 1.7.32 + 1.7.35 4.13.2 - 1.2.6 + 1.2.10 3.12.0 From a8957cc6dda55f2b5f13c7650c790754ccb8c7e6 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 1 Feb 2022 18:10:19 +0100 Subject: [PATCH 115/133] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffee6d09..025a7054 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ Install it as a maven dependency: com.onelogin java-saml - 2.8.0 + 2.9.0 ``` From 988de2deb4f016c214e84e3c6a36e53fbb1dca45 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 1 Feb 2022 18:14:50 +0100 Subject: [PATCH 116/133] [maven-release-plugin] prepare release v2.9.0 --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 1a23fc65..858d4905 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.1-SNAPSHOT + 2.9.0 jar diff --git a/pom.xml b/pom.xml index beeef0c0..9a758a02 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.onelogin java-saml-toolkit - 2.8.1-SNAPSHOT + 2.9.0 pom OneLogin java-saml Toolkit Project @@ -157,7 +157,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - HEAD + v2.9.0 diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 9c6185e3..8d436f3a 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.8.1-SNAPSHOT + 2.9.0 java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 470d0048..00f7e1e2 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.1-SNAPSHOT + 2.9.0 java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index e517b4de..52e785d3 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.8.1-SNAPSHOT + 2.9.0 jar From eeaf06e661828909784fe2c22bec21a2a06f757d Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 1 Feb 2022 18:14:55 +0100 Subject: [PATCH 117/133] [maven-release-plugin] prepare for next development iteration --- core/pom.xml | 2 +- pom.xml | 4 ++-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 858d4905..1ffe1f1b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.9.0 + 2.9.1-SNAPSHOT jar diff --git a/pom.xml b/pom.xml index 9a758a02..1e4cabea 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.onelogin java-saml-toolkit - 2.9.0 + 2.9.1-SNAPSHOT pom OneLogin java-saml Toolkit Project @@ -157,7 +157,7 @@ scm:git:git@github.com:onelogin/java-saml.git scm:git:git@github.com:onelogin/java-saml.git https://github.com/onelogin/java-saml - v2.9.0 + HEAD diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 8d436f3a..13afeb5b 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-tookit-samples - 2.9.0 + 2.9.1-SNAPSHOT java-saml-tookit-jspsample diff --git a/samples/pom.xml b/samples/pom.xml index 00f7e1e2..799ec247 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.9.0 + 2.9.1-SNAPSHOT java-saml-tookit-samples diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 52e785d3..47e8b5d2 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -3,7 +3,7 @@ com.onelogin java-saml-toolkit - 2.9.0 + 2.9.1-SNAPSHOT jar From c5104400509db30aa0ff4296cba7cdca02807189 Mon Sep 17 00:00:00 2001 From: Haavar Valeur Date: Mon, 7 Feb 2022 12:40:53 -0800 Subject: [PATCH 118/133] Updated org.apache.santuario:xmlsec to version 2.3.0 --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 1ffe1f1b..92baa25e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -60,7 +60,7 @@ org.apache.santuario xmlsec - 2.2.3 + 2.3.0 commons-codec From aaf19433d5eb77fd76116ae70ced8e77291b86ee Mon Sep 17 00:00:00 2001 From: luozhouyang Date: Sun, 17 Apr 2022 17:30:15 +0800 Subject: [PATCH 119/133] Fixed README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 025a7054..b36c7acb 100644 --- a/README.md +++ b/README.md @@ -649,6 +649,7 @@ If we don't want that processSLO to destroy the session, pass the keepLocalSessi #### Initiate SLO In order to send a Logout Request to the IdP: + ```java Auth auth = new Auth(request, response); @@ -673,12 +674,14 @@ if (session.getAttribute("sessionIndex") != null) { sessionIndex = session.getAttribute("sessionIndex").toString(); } auth.logout(null, new LogoutRequestParams(sessionIndex, nameId, nameIdFormat)); -```java +``` + The Logout Request will be sent signed or unsigned based on the security settings 'onelogin.saml2.security.logoutrequest_signed' The IdP will return the Logout Response through the user's client to the Single Logout Service of the SP. We can set a 'RelayState' parameter containing a return url to the login function: + ```java String returnUrl = 'https://example.com'; auth.logout(relayState=returnUrl) From ad84787f20ec931819917aa65356b5c73965a4a3 Mon Sep 17 00:00:00 2001 From: Bryan Vestey Date: Fri, 12 Aug 2022 09:51:55 -0700 Subject: [PATCH 120/133] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 025a7054..4dc9f0c9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Build Status](https://travis-ci.org/onelogin/java-saml.svg?branch=master)](https://travis-ci.org/onelogin/java-saml) [![Coverage Status](https://coveralls.io/repos/github/onelogin/java-saml/badge.svg?branch=master)](https://coveralls.io/github/onelogin/java-saml?branch=master) +## **Notice:** This project is currently not under active development, please see [#388](https://github.com/onelogin/java-saml/issues/388) for more information. + Add SAML support to your Java applications using this library. Forget those complicated libraries and use that open source library provided and supported by OneLogin Inc. From 591c358d829c1bb88b409f1b033801f78402df0a Mon Sep 17 00:00:00 2001 From: Takayuki Maruyama Date: Mon, 17 Jan 2022 23:38:12 +0000 Subject: [PATCH 121/133] Use java.time instead of joda-time fix #201 --- core/pom.xml | 7 - .../onelogin/saml2/authn/SamlResponse.java | 40 ++--- .../onelogin/saml2/logout/LogoutRequest.java | 8 +- .../onelogin/saml2/logout/LogoutResponse.java | 2 +- .../java/com/onelogin/saml2/util/Util.java | 137 +++++++++++------- .../saml2/test/authn/AuthnResponseTest.java | 32 ++-- .../onelogin/saml2/test/util/UtilsTest.java | 17 ++- .../saml2/util/DateTimeTestUtils.java | 24 +++ toolkit/pom.xml | 7 - .../main/java/com/onelogin/saml2/Auth.java | 7 +- .../com/onelogin/saml2/test/AuthTest.java | 6 +- 11 files changed, 160 insertions(+), 127 deletions(-) create mode 100644 core/src/test/java/com/onelogin/saml2/util/DateTimeTestUtils.java diff --git a/core/pom.xml b/core/pom.xml index 92baa25e..123ec966 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 8fc66d8a..a7996096 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 cacd7bf8..903cd2db 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 7199fae9..4eebfd74 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 8a6be158..91249e8b 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 + * @throwsa 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 47e8b5d2..a57711bc 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 511914b8..0126ec35 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()); } /** From 009ff694754f19426fb77760de2687f5826ccf9b Mon Sep 17 00:00:00 2001 From: Takayuki Maruyama Date: Wed, 21 Sep 2022 14:55:41 +0000 Subject: [PATCH 122/133] fix typo --- core/src/main/java/com/onelogin/saml2/util/Util.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 91249e8b..0502e10f 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -1844,7 +1844,7 @@ public static long parseDuration(String duration) throws DateTimeParseException * * @return the new timestamp, after the duration is applied In Seconds. * - * @throwsa DateTimeParseException + * @throws DateTimeParseException */ public static long parseDuration(String durationString, long timestamp) throws DateTimeParseException { boolean haveMinus = false; From baee4e0a0c6ab6411034d2413e837f4403f1a11f Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 14:37:38 -0800 Subject: [PATCH 123/133] Remove references to onelogin support. --- README.md | 20 ++-- core/pom.xml | 2 +- .../onelogin/saml2/authn/AuthnRequest.java | 30 +++--- .../onelogin/saml2/authn/SamlResponse.java | 10 +- .../saml2/exception/SAMLException.java | 10 +- .../onelogin/saml2/logout/LogoutRequest.java | 92 +++++++++---------- .../onelogin/saml2/logout/LogoutResponse.java | 34 +++---- .../model/AttributeConsumingService.java | 10 +- .../com/onelogin/saml2/model/Contact.java | 16 ++-- .../saml2/model/KeyStoreSettings.java | 6 +- .../onelogin/saml2/model/Organization.java | 16 ++-- .../saml2/model/RequestedAttribute.java | 16 ++-- .../saml2/model/SamlResponseStatus.java | 14 +-- .../saml2/settings/IdPMetadataParser.java | 40 ++++---- .../com/onelogin/saml2/settings/Metadata.java | 6 +- .../saml2/settings/Saml2Settings.java | 62 ++++++------- .../saml2/settings/SettingsBuilder.java | 70 +++++++------- .../com/onelogin/saml2/util/Constants.java | 20 ++-- .../onelogin/saml2/util/SchemaFactory.java | 10 +- .../java/com/onelogin/saml2/util/Util.java | 10 +- .../resources/config/config.adfs.properties | 16 ++-- .../resources/config/config.all.properties | 18 ++-- .../config/config.all_specialchars.properties | 16 ++-- .../config.allowduplicatednames.properties | 18 ++-- .../config/config.certfile.properties | 20 ++-- .../config/config.certstring.properties | 18 ++-- .../config/config.different.properties | 18 ++-- .../config/config.invalidcontacts.properties | 16 ++-- .../config.invalidspcertstring.properties | 16 ++-- .../config.knownIdpPrivateKey.properties | 12 +-- .../resources/config/config.my.properties | 18 ++-- .../config/config.mywithmulticert.properties | 18 ++-- .../config/config.mywithnocert.properties | 18 ++-- .../config/config.newattack.properties | 20 ++-- .../config/config.newattack2.properties | 20 ++-- .../config/config.samecerts.properties | 18 ++-- .../config/config.somevaluesempty.properties | 18 ++-- pom.xml | 14 +-- samples/java-saml-tookit-jspsample/pom.xml | 2 +- .../main/resources/onelogin.saml.properties | 22 ++--- .../src/main/webapp/acs.jsp | 20 ++-- .../src/main/webapp/attrs.jsp | 18 ++-- .../src/main/webapp/index.jsp | 4 +- .../src/main/webapp/sls.jsp | 12 +-- samples/pom.xml | 2 +- toolkit/pom.xml | 2 +- .../main/java/com/onelogin/saml2/Auth.java | 40 ++++---- .../onelogin/saml2/servlet/ServletUtils.java | 8 +- 48 files changed, 466 insertions(+), 470 deletions(-) diff --git a/README.md b/README.md index 4dc9f0c9..009fac58 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,8 @@ -# OneLogin's SAML Java Toolkit +# SAML Java Toolkit [![Build Status](https://travis-ci.org/onelogin/java-saml.svg?branch=master)](https://travis-ci.org/onelogin/java-saml) [![Coverage Status](https://coveralls.io/repos/github/onelogin/java-saml/badge.svg?branch=master)](https://coveralls.io/github/onelogin/java-saml?branch=master) -## **Notice:** This project is currently not under active development, please see [#388](https://github.com/onelogin/java-saml/issues/388) for more information. - Add SAML support to your Java applications using this library. -Forget those complicated libraries and use that open source library provided and supported by OneLogin Inc. 2.8.0 uses xmlsec 2.2.3 which fixes [CVE-2021-40690](https://snyk.io/vuln/SNYK-JAVA-ORGAPACHESANTUARIO-1655558) @@ -87,7 +84,7 @@ since 2002, but lately it has become popular due to its advantages as follows: ## General description -OneLogin's SAML Java toolkit lets you turn a Java application into a SP +SAML Java toolkit lets you turn a Java application into a SP (Service Provider) that can be connected to an IdP (Identity Provider). Supports: @@ -108,7 +105,6 @@ Key features: * **Easy to use** - Programmer will be allowed to code high-level and low-level programming; 2 easy-to-use APIs are available. * **Tested** - Thoroughly tested. - * **Popular** - OneLogin's customers use it. Add easy support to your java web projects. ## Security warning @@ -198,7 +194,7 @@ Select a [Tomcat Server](http://crunchify.com/step-by-step-guide-to-setup-and-in ## Getting started ### Learning the toolkit -OneLogin's new SAML Java SAML Toolkit contains different folders (core, toolkit, samples) and some files. +Java SAML Toolkit contains different folders (core, toolkit, samples) and some files. Let's start describing them: @@ -261,7 +257,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-tookit-jspsample/me onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-tookit-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -270,7 +266,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-tookit-jspsample/sls.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -326,7 +322,7 @@ onelogin.saml2.idp.entityid = onelogin.saml2.idp.single_sign_on_service.url = # SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -340,7 +336,7 @@ onelogin.saml2.idp.single_logout_service.url = onelogin.saml2.idp.single_logout_service.response.url = # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -755,7 +751,7 @@ In order to avoid replay attacks, you can store the ID of the SAML messages alre Get the ID of the last processed message with the getLastMessageId method of the Auth object. ## Demo included in the toolkit -The Onelogin's Java Toolkit allows you to provide the settings in a unique file as described at the [Settings section](https://github.com/onelogin/java-saml/#Settings). +The Java Toolkit allows you to provide the settings in a unique file as described at the [Settings section](https://github.com/onelogin/java-saml/#Settings). #### SP setup Configure the SP part and review the metadata of the IdP and complete the IdP info. Later configure how the toolkit will work enabling/disabling the security settings. diff --git a/core/pom.xml b/core/pom.xml index 92baa25e..b00d34e2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ jar - OneLogin java-saml Toolkit Core + java-saml Toolkit Core java-saml-core diff --git a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java index b5f3811f..8bb89b2b 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -17,7 +17,7 @@ import com.onelogin.saml2.util.Util; /** - * AuthNRequest class of OneLogin's Java Toolkit. + * AuthNRequest class of Java Toolkit. * * A class that implements SAML 2 Authentication Request */ @@ -79,7 +79,7 @@ public AuthnRequest(Saml2Settings settings) { public AuthnRequest(Saml2Settings settings, boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, String nameIdValueReq) { this(settings, new AuthnRequestParams(forceAuthn, isPassive, setNameIdPolicy, nameIdValueReq)); } - + /** * Constructs the AuthnRequest object. * @@ -126,7 +126,7 @@ public AuthnRequest(Saml2Settings settings, AuthnRequestParams params) { * This method is invoked at construction time, after all the other fields of * this class have already been initialised. Its default implementation simply * returns the input XML as-is, with no change. - * + * * @param authnRequestXml * the XML produced for this AuthnRequest by the standard * implementation provided by {@link AuthnRequest} @@ -144,10 +144,10 @@ protected String postProcessXml(final String authnRequestXml, final AuthnRequest /** * @return the base64 encoded unsigned AuthnRequest (deflated or not) * - * @param deflated + * @param deflated * If deflated or not the encoded AuthnRequest * - * @throws IOException + * @throws IOException */ public String getEncodedAuthnRequest(Boolean deflated) throws IOException { String encodedAuthnRequest; @@ -161,18 +161,18 @@ public String getEncodedAuthnRequest(Boolean deflated) throws IOException { } return encodedAuthnRequest; } - + /** * @return base64 encoded, unsigned AuthnRequest (deflated or not) - * - * @throws IOException + * + * @throws IOException */ public String getEncodedAuthnRequest() throws IOException { return getEncodedAuthnRequest(null); } /** - * @return unsigned plain-text AuthnRequest. + * @return unsigned plain-text AuthnRequest. */ public String getAuthnRequestXml() { return authnRequestString; @@ -185,9 +185,9 @@ public String getAuthnRequestXml() { * the authentication request input parameters * @param settings * Saml2Settings object. Setting data - * - * @return the StrSubstitutor object of the AuthnRequest - */ + * + * @return the StrSubstitutor object of the AuthnRequest + */ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Settings settings) { Map valueMap = new HashMap(); @@ -242,7 +242,7 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti if (organization != null) { String displayName = organization.getOrgDisplayName(); if (!displayName.isEmpty()) { - providerStr = " ProviderName=\""+ Util.toXml(displayName) + "\""; + providerStr = " ProviderName=\""+ Util.toXml(displayName) + "\""; } } valueMap.put("providerStr", providerStr); @@ -288,10 +288,10 @@ public String getId() { return id; } - + /** * Returns the issue instant of this message. - * + * * @return a new {@link Calendar} instance carrying the issue instant of this message */ public Calendar getIssueInstant() { 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 8fc66d8a..03932b2f 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java +++ b/core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java @@ -37,7 +37,7 @@ import com.onelogin.saml2.util.Util; /** - * SamlResponse class of OneLogin's Java Toolkit. + * SamlResponse class of Java Toolkit. * * A class that implements SAML 2 Authentication Response parser/validator */ @@ -752,7 +752,7 @@ public String getResponseIssuer() throws XPathExpressionException, ValidationErr } return null; } - + /** * Gets the Assertion Issuer. * @@ -777,7 +777,7 @@ public String getAssertionIssuer() throws XPathExpressionException, ValidationEr throw new ValidationError("Issuer of the Assertion not found or multiple.", ValidationError.ISSUER_NOT_FOUND_IN_ASSERTION); } } - + /** * Gets the Issuers (from Response and Assertion). If the same issuer appears * both in the Response and in the Assertion (as it should), the returned list @@ -1102,11 +1102,11 @@ public String getError() { public Exception getValidationException() { return validationException; } - + /** * Sets the validation exception that this {@link SamlResponse} should return * when a validation error occurs. - * + * * @param validationException * the validation exception to set */ diff --git a/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java b/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java index a1e1547f..cc05147c 100644 --- a/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java +++ b/core/src/main/java/com/onelogin/saml2/exception/SAMLException.java @@ -1,7 +1,7 @@ package com.onelogin.saml2.exception; /** - * Top-level exception class for the OneLogin SAML client. + * Top-level exception class for the SAML client. */ public class SAMLException extends Exception { @@ -10,7 +10,7 @@ public class SAMLException extends Exception { /** * Construct a SAMLException with the provided error message. * - * @param message + * @param message * The human-readable error message associated with this exception. */ public SAMLException(String message) { @@ -33,12 +33,12 @@ public SAMLException(Throwable cause) { * * @param message * The human-readable error message associated with this exception. - * - * @param cause + * + * @param cause * The upstream cause associated with this exception. */ public SAMLException(String message, Throwable cause) { super(message, cause); } -} \ No newline at end of file +} 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 cacd7bf8..954ae9e9 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java @@ -29,7 +29,7 @@ import com.onelogin.saml2.util.SchemaFactory; /** - * LogoutRequest class of OneLogin's Java Toolkit. + * LogoutRequest class of Java Toolkit. * * A class that implements SAML 2 Logout Request builder/parser/validator */ @@ -71,7 +71,7 @@ public class LogoutRequest { /** * After validation, if it fails this property has the cause of the problem - */ + */ private Exception validationException; /** @@ -104,19 +104,19 @@ public class LogoutRequest { public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat, String nameIdNameQualifier, String nameIdSPNameQualifier) { this.settings = settings; this.request = request; - + String samlLogoutRequest = null; - + if (request != null) { samlLogoutRequest = request.getParameter("SAMLRequest"); currentUrl = request.getRequestURL(); } - + if (samlLogoutRequest == null) { LogoutRequestParams params = new LogoutRequestParams(sessionIndex, nameId, nameIdFormat, nameIdNameQualifier, nameIdSPNameQualifier); id = Util.generateUniqueID(settings.getUniqueIDPrefix()); issueInstant = Calendar.getInstance(); - + StrSubstitutor substitutor = generateSubstitutor(params, settings); logoutRequestString = postProcessXml(substitutor.replace(getLogoutRequestTemplate()), params, settings); } else { @@ -264,7 +264,7 @@ public LogoutRequest(Saml2Settings settings, LogoutRequestParams params) { * scenario only), after all the other fields of this class have already been * initialised. Its default implementation simply returns the input XML as-is, * with no change. - * + * * @param logoutRequestXml * the XML produced for this LogoutRequest by the standard * implementation provided by {@link LogoutRequest} @@ -282,10 +282,10 @@ protected String postProcessXml(final String logoutRequestXml, final LogoutReque /** * @return the base64 encoded unsigned Logout Request (deflated or not) * - * @param deflated + * @param deflated * If deflated or not the encoded Logout Request * - * @throws IOException + * @throws IOException */ public String getEncodedLogoutRequest(Boolean deflated) throws IOException { String encodedLogoutRequest; @@ -299,11 +299,11 @@ public String getEncodedLogoutRequest(Boolean deflated) throws IOException { } return encodedLogoutRequest; } - + /** * @return the base64 encoded unsigned Logout Request (deflated or not) * - * @throws IOException + * @throws IOException */ public String getEncodedLogoutRequest() throws IOException { return getEncodedLogoutRequest(null); @@ -323,13 +323,13 @@ public String getLogoutRequestXml() { * the logout request input parameters * @param settings * Saml2Settings object. Setting data - * + * * @return the StrSubstitutor object of the LogoutRequest */ private StrSubstitutor generateSubstitutor(LogoutRequestParams params, Saml2Settings settings) { Map valueMap = new HashMap(); - valueMap.put("id", Util.toXml(id)); + valueMap.put("id", Util.toXml(id)); String issueInstantString = Util.formatDateTime(issueInstant.getTimeInMillis()); valueMap.put("issueInstant", issueInstantString); @@ -356,11 +356,11 @@ private StrSubstitutor generateSubstitutor(LogoutRequestParams params, Saml2Sett } } else { nameId = settings.getIdpEntityId(); - nameIdFormat = Constants.NAMEID_ENTITY; + nameIdFormat = Constants.NAMEID_ENTITY; } // From saml-core-2.0-os 8.3.6, when the entity Format is used: "The NameQualifier, SPNameQualifier, and - // SPProvidedID attributes MUST be omitted. + // SPProvidedID attributes MUST be omitted. if (nameIdFormat != null && nameIdFormat.equals(Constants.NAMEID_ENTITY)) { nameQualifier = null; spNameQualifier = null; @@ -424,7 +424,7 @@ public Boolean isValid() { if (this.request == null) { throw new Exception("The HttpRequest of the current host was not established"); } - + if (this.currentUrl == null || this.currentUrl.isEmpty()) { throw new Exception("The URL of the current host was not established"); } @@ -435,7 +435,7 @@ public Boolean isValid() { if (settings.isStrict()) { Element rootElement = logoutRequestDocument.getDocumentElement(); - rootElement.normalize(); + rootElement.normalize(); if (settings.getWantXMLValidation()) { if (!Util.validateXML(logoutRequestDocument, SchemaFactory.SAML_SCHEMA_PROTOCOL_2_0)) { @@ -479,10 +479,10 @@ public Boolean isValid() { throw new ValidationError("The Message of the Logout Request is not signed and the SP requires it", ValidationError.NO_SIGNED_MESSAGE); } } - + if (signature != null && !signature.isEmpty()) { X509Certificate cert = settings.getIdpx509cert(); - + List certList = new ArrayList(); List multipleCertList = settings.getIdpx509certMulti(); @@ -524,9 +524,9 @@ public Boolean isValid() { throw new ValidationError("Signature validation failed. Logout Request rejected", ValidationError.INVALID_SIGNATURE); } } - + LOGGER.debug("LogoutRequest validated --> " + logoutRequestString); - return true; + return true; } catch (Exception e) { validationException = e; LOGGER.debug("LogoutRequest invalid --> " + logoutRequestString); @@ -589,7 +589,7 @@ public static String getId(String samlLogoutRequestString) { Document doc = Util.loadXML(samlLogoutRequestString); return getId(doc); } - + /** * Returns the issue instant of the Logout Request Document. * @@ -618,7 +618,7 @@ public static Calendar getIssueInstant(String samlLogoutRequestString) { public static Map getNameIdData(Document samlLogoutRequestDocument, PrivateKey key) throws Exception { return getNameIdData(samlLogoutRequestDocument, key, false); } - + /** * Gets the NameID Data from the the Logout Request Document. * @@ -637,7 +637,7 @@ public static Map getNameIdData(Document samlLogoutRequestDocume NodeList encryptedIDNodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:EncryptedID"); NodeList nameIdNodes; Element nameIdElem; - + if (encryptedIDNodes.getLength() == 1) { if (key == null) { throw new SettingsException("Key is required in order to decrypt the NameID", SettingsException.PRIVATE_KEY_NOT_FOUND); @@ -650,7 +650,7 @@ public static Map getNameIdData(Document samlLogoutRequestDocume if (nameIdNodes == null || nameIdNodes.getLength() != 1) { throw new Exception("Not able to decrypt the EncryptedID and get a NameID"); } - } + } else { nameIdNodes = Util.query(samlLogoutRequestDocument, "/samlp:LogoutRequest/saml:NameID"); } @@ -660,9 +660,9 @@ public static Map getNameIdData(Document samlLogoutRequestDocume } else { throw new ValidationError("No name id found in Logout Request.", ValidationError.NO_NAMEID); } - + Map nameIdData = new HashMap(); - + if (nameIdElem != null) { String value = nameIdElem.getTextContent(); if(value != null && trimValue) { @@ -723,10 +723,10 @@ public static Map getNameIdData(String samlLogoutRequestString, * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. - * + * * @param key * The SP key to decrypt the NameID if encrypted - * + * * @return the Name ID value * * @throws Exception @@ -740,10 +740,10 @@ public static String getNameId(Document samlLogoutRequestDocument, PrivateKey ke * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. - * + * * @param key * The SP key to decrypt the NameID if encrypted - * + * * @param trimValue * whether the extracted Name ID value should be trimmed * @@ -771,7 +771,7 @@ public static String getNameId(Document samlLogoutRequestDocument, PrivateKey ke public static String getNameId(Document samlLogoutRequestDocument) throws Exception { return getNameId(samlLogoutRequestDocument, null); } - + /** * Gets the NameID value provided from the SAML Logout Request String. * @@ -821,10 +821,10 @@ public static String getNameId(String samlLogoutRequestString, PrivateKey key, b public static String getNameId(String samlLogoutRequestString) throws Exception { return getNameId(samlLogoutRequestString, null); } - + /** * Gets the Issuer from Logout Request Document. - * + * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. * @@ -838,7 +838,7 @@ public static String getIssuer(Document samlLogoutRequestDocument) throws XPathE /** * Gets the Issuer from Logout Request Document. - * + * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. * @param trim @@ -864,12 +864,12 @@ public static String getIssuer(Document samlLogoutRequestDocument, boolean trim) /** * Gets the Issuer from Logout Request String. - * + * * @param samlLogoutRequestString * A Logout Request string. * * @return the issuer of the logout request - * + * * @throws XPathExpressionException */ public static String getIssuer(String samlLogoutRequestString) throws XPathExpressionException { @@ -878,14 +878,14 @@ public static String getIssuer(String samlLogoutRequestString) throws XPathExpre /** * Gets the Issuer from Logout Request String. - * + * * @param samlLogoutRequestString * A Logout Request string. * @param trim * whether the extracted issuer value should be trimmed * * @return the issuer of the logout request - * + * * @throws XPathExpressionException */ public static String getIssuer(String samlLogoutRequestString, boolean trim) throws XPathExpressionException { @@ -896,7 +896,7 @@ public static String getIssuer(String samlLogoutRequestString, boolean trim) thr /** * Gets the SessionIndexes from the LogoutRequest. - * + * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. * @return the SessionIndexes @@ -909,7 +909,7 @@ public static List getSessionIndexes(Document samlLogoutRequestDocument) /** * Gets the SessionIndexes from the LogoutRequest. - * + * * @param samlLogoutRequestDocument * A DOMDocument object loaded from the SAML Logout Request. * @param trim @@ -939,7 +939,7 @@ public static List getSessionIndexes(Document samlLogoutRequestDocument, /** * Gets the SessionIndexes from the LogoutRequest. - * + * * @param samlLogoutRequestString * A Logout Request string. * @return the SessionIndexes @@ -952,7 +952,7 @@ public static List getSessionIndexes(String samlLogoutRequestString) thr /** * Gets the SessionIndexes from the LogoutRequest. - * + * * @param samlLogoutRequestString * A Logout Request string. * @param trim @@ -970,7 +970,7 @@ public static List getSessionIndexes(String samlLogoutRequestString, boo /** * After execute a validation process, if fails this method returns the cause * - * @return the cause of the validation error + * @return the cause of the validation error */ public String getError() { if (validationException != null) { @@ -991,7 +991,7 @@ public Exception getValidationException() { /** * Sets the validation exception that this {@link LogoutRequest} should return * when a validation error occurs. - * + * * @param validationException * the validation exception to set */ @@ -1009,7 +1009,7 @@ public String getId() /** * Returns the issue instant of this message. - * + * * @return a new {@link Calendar} instance carrying the issue instant of this message */ public Calendar getIssueInstant() { 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 7199fae9..8fc1bc65 100644 --- a/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java +++ b/core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java @@ -29,7 +29,7 @@ import com.onelogin.saml2.util.Util; /** - * LogoutResponse class of OneLogin's Java Toolkit. + * LogoutResponse class of Java Toolkit. * * A class that implements SAML 2 Logout Response builder/parser/validator */ @@ -101,12 +101,12 @@ public LogoutResponse(Saml2Settings settings, HttpRequest request) { samlLogoutResponse = request.getParameter("SAMLResponse"); } - if (samlLogoutResponse != null && !samlLogoutResponse.isEmpty()) { + if (samlLogoutResponse != null && !samlLogoutResponse.isEmpty()) { logoutResponseString = Util.base64decodedInflated(samlLogoutResponse); logoutResponseDocument = Util.loadXML(logoutResponseString); } } - + /** * Constructs the LogoutResponse object when a new response should be generated * and sent. @@ -146,7 +146,7 @@ public String getEncodedLogoutResponse(Boolean deflated) throws IOException { } return encodedLogoutResponse; } - + /** * @return the base64 encoded, unsigned Logout Response (deflated or not) * @@ -200,7 +200,7 @@ public Boolean isValid(String requestId) { if (settings.isStrict()) { Element rootElement = logoutResponseDocument.getDocumentElement(); - rootElement.normalize(); + rootElement.normalize(); if (settings.getWantXMLValidation()) { if (!Util.validateXML(this.logoutResponseDocument, SchemaFactory.SAML_SCHEMA_PROTOCOL_2_0)) { @@ -359,7 +359,7 @@ public SamlResponseStatus getSamlResponseStatus() throws ValidationError * Xpath Expression * * @return DOMNodeList The queried nodes - * @throws XPathExpressionException + * @throws XPathExpressionException */ protected NodeList query (String query) throws XPathExpressionException { return Util.query(this.logoutResponseDocument, query, null); @@ -398,7 +398,7 @@ public void build(String inResponseTo, SamlResponseStatus responseStatus) { * Generates a Logout Response XML string. * * @param inResponseTo - * InResponseTo attribute value to bet set at the Logout Response. + * InResponseTo attribute value to bet set at the Logout Response. * @param statusCode * String StatusCode to be set on the LogoutResponse * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} @@ -439,10 +439,10 @@ public void build(String inResponseTo, String statusCode) { public void build(String inResponseTo) { build(inResponseTo, Constants.STATUS_SUCCESS); } - + /** * Generates a Logout Response XML string. - * + * * @deprecated use {@link #LogoutResponse(Saml2Settings, LogoutResponseParams)} * instead, in which case this method becomes completely useless; * indeed, invoking this method in an outgoing logout response @@ -458,7 +458,7 @@ public void build(String inResponseTo) { @Deprecated public void build() { build(null); - } + } /** * Allows for an extension class to post-process the LogoutResponse XML @@ -467,7 +467,7 @@ public void build() { * This method is invoked by {@link #build(String, String)} (and all of its * overloadings) and hence only in the logout response sending scenario. Its * default implementation simply returns the input XML as-is, with no change. - * + * * @param logoutResponseXml * the XML produced for this LogoutResponse by the standard * implementation provided by {@link LogoutResponse} @@ -482,7 +482,7 @@ protected String postProcessXml(final String logoutResponseXml, final LogoutResp final Saml2Settings settings) { return logoutResponseXml; } - + /** * Substitutes LogoutResponse variables within a string by values. * @@ -513,7 +513,7 @@ private StrSubstitutor generateSubstitutor(LogoutResponseParams params, Saml2Set if (inResponseTo != null) { inResponseStr = " InResponseTo=\"" + Util.toXml(inResponseTo) + "\""; } - valueMap.put("inResponseStr", inResponseStr); + valueMap.put("inResponseStr", inResponseStr); StringBuilder statusStr = new StringBuilder("(); + this.requestedAttributes = new ArrayList(); } /** @@ -46,7 +46,7 @@ public AttributeConsumingService(String serviceName, String serviceDescription) public final void addRequestedAttribute(RequestedAttribute attr) { this.requestedAttributes.add(attr); } - + /** * @return string the service name */ @@ -68,4 +68,4 @@ public final List getRequestedAttributes() { return requestedAttributes; } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/onelogin/saml2/model/Contact.java b/core/src/main/java/com/onelogin/saml2/model/Contact.java index eb9a52ee..8c03ac4a 100644 --- a/core/src/main/java/com/onelogin/saml2/model/Contact.java +++ b/core/src/main/java/com/onelogin/saml2/model/Contact.java @@ -5,9 +5,9 @@ import java.util.List; /** - * Contact class of OneLogin's Java Toolkit. + * Contact class of Java Toolkit. * - * A class that stores contact info + * A class that stores contact info */ public class Contact { /** @@ -19,12 +19,12 @@ public class Contact { * Contact company */ private final String company; - + /** * Contact given name */ private final String givenName; - + /** * Contact surname */ @@ -39,7 +39,7 @@ public class Contact { * Contact phone number */ private final List telephoneNumbers; - + /** * Constructor to specify minimal contact data. *

@@ -114,14 +114,14 @@ public final List getEmailAddresses() { public final String getGivenName() { return givenName; } - + /** * @return the contact surname */ public final String getSurName() { return surName; } - + /** * @return the contact company */ @@ -135,4 +135,4 @@ public final String getCompany() { public final List getTelephoneNumbers() { return telephoneNumbers; } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/onelogin/saml2/model/KeyStoreSettings.java b/core/src/main/java/com/onelogin/saml2/model/KeyStoreSettings.java index 0f89fa88..7659f44f 100644 --- a/core/src/main/java/com/onelogin/saml2/model/KeyStoreSettings.java +++ b/core/src/main/java/com/onelogin/saml2/model/KeyStoreSettings.java @@ -3,7 +3,7 @@ import java.security.KeyStore; /** - * KeyStore class of OneLogin's Java Toolkit. + * KeyStore class of Java Toolkit. * * A class that stores KeyStore details for Certificates and Private Key */ @@ -28,7 +28,7 @@ public class KeyStoreSettings { * * @param keyStore * stores certificates and key - * + * * @param spAlias * Alias for SP key entry * @@ -62,4 +62,4 @@ public final String getSpKeyPass() { return spKeyPass; } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/onelogin/saml2/model/Organization.java b/core/src/main/java/com/onelogin/saml2/model/Organization.java index c60599b4..b935391a 100644 --- a/core/src/main/java/com/onelogin/saml2/model/Organization.java +++ b/core/src/main/java/com/onelogin/saml2/model/Organization.java @@ -6,9 +6,9 @@ /** - * Organization class of OneLogin's Java Toolkit. + * Organization class of Java Toolkit. * - * A class that stores organization info + * A class that stores organization info */ public class Organization { /** @@ -25,7 +25,7 @@ public class Organization { * Organization URL */ private final String orgUrl; - + /** * Organization lang attribute */ @@ -46,7 +46,7 @@ public class Organization { public Organization(String orgName, String orgDisplayName, URL orgUrl, String orgLangAttribute) { this(orgName, orgDisplayName, orgUrl != null ? orgUrl.toString() : "", orgLangAttribute); } - + /** * Constructor
* Default the lang attribute to "en" @@ -80,7 +80,7 @@ public Organization(String orgName, String orgDisplayName, String orgUrl, String this.orgUrl = orgUrl != null ? orgUrl : ""; this.orgLangAttribute = StringUtils.defaultIfBlank(orgLangAttribute, "en"); } - + /** * Constructor
* Default the lang attribute to "en" @@ -116,13 +116,13 @@ public final String getOrgDisplayName() { public final String getOrgUrl() { return orgUrl; } - + /** * @return string the lang attribute */ public final String getOrgLangAttribute() { return orgLangAttribute; - } + } /** * Compare with another organization @@ -133,5 +133,5 @@ public final String getOrgLangAttribute() { */ public final Boolean equalsTo(Organization org) { return orgName.equals(org.getOrgName()) && orgDisplayName.equals(org.getOrgDisplayName()) && orgUrl.equals(org.getOrgUrl()) && orgLangAttribute.equals(org.getOrgLangAttribute()); - } + } } diff --git a/core/src/main/java/com/onelogin/saml2/model/RequestedAttribute.java b/core/src/main/java/com/onelogin/saml2/model/RequestedAttribute.java index 2079b57e..e0d37de7 100644 --- a/core/src/main/java/com/onelogin/saml2/model/RequestedAttribute.java +++ b/core/src/main/java/com/onelogin/saml2/model/RequestedAttribute.java @@ -4,9 +4,9 @@ /** - * RequestedAttribute class of OneLogin's Java Toolkit. + * RequestedAttribute class of Java Toolkit. * - * A class that stores RequestedAttribute of the AttributeConsumingService + * A class that stores RequestedAttribute of the AttributeConsumingService */ public class RequestedAttribute { /** @@ -23,7 +23,7 @@ public class RequestedAttribute { * If the attribute is or not required */ private final Boolean isRequired; - + /** * NameFormat of the attribute */ @@ -33,7 +33,7 @@ public class RequestedAttribute { * Values of the attribute */ private final List attributeValues; - + /** * Constructor * @@ -55,7 +55,7 @@ public RequestedAttribute(String name, String friendlyName, Boolean isRequired, this.nameFormat = nameFormat; this.attributeValues = attributeValues; } - + /** * @return string the RequestedAttribute name */ @@ -76,18 +76,18 @@ public final String getFriendlyName() { public final Boolean isRequired() { return isRequired; } - + /** * @return string the RequestedAttribute nameformat */ public final String getNameFormat() { return nameFormat; } - + /** * @return string the RequestedAttribute nameformat */ public final List getAttributeValues() { return attributeValues; } -} \ No newline at end of file +} diff --git a/core/src/main/java/com/onelogin/saml2/model/SamlResponseStatus.java b/core/src/main/java/com/onelogin/saml2/model/SamlResponseStatus.java index 7552380a..1a4e6e78 100644 --- a/core/src/main/java/com/onelogin/saml2/model/SamlResponseStatus.java +++ b/core/src/main/java/com/onelogin/saml2/model/SamlResponseStatus.java @@ -2,7 +2,7 @@ /** - * SamlResponseStatus class of OneLogin's Java Toolkit. + * SamlResponseStatus class of Java Toolkit. * * A class that stores the SAML response status info */ @@ -54,7 +54,7 @@ public String getStatusCode() { /** * Set the status code - * + * * @param statusCode * String. Status code */ @@ -88,8 +88,8 @@ public String getStatusMessage() { /** * Set the status message - * - * @param statusMessage + * + * @param statusMessage * String. Status message */ public void setStatusMessage(String statusMessage) { @@ -98,11 +98,11 @@ public void setStatusMessage(String statusMessage) { /** * Compare the status code - * - * @param status + * + * @param status * String. Status code * - * @return boolean checks the status code + * @return boolean checks the status code */ public boolean is(String status) { return statusCode != null && !statusCode.isEmpty() && statusCode.equals(status); diff --git a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java index d2d46426..42ee439c 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java +++ b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java @@ -20,7 +20,7 @@ import com.onelogin.saml2.util.Util; /** - * IdPMetadataParser class of OneLogin's Java Toolkit. + * IdPMetadataParser class of Java Toolkit. * * A class that implements the settings parser from IdP Metadata * @@ -36,7 +36,7 @@ public class IdPMetadataParser { /** * Get IdP Metadata Info from XML Document - * + * * @param xmlDocument * XML document hat contains IdP metadata * @param entityId @@ -47,7 +47,7 @@ public class IdPMetadataParser { * Parse specific binding SSO endpoint. * @param desiredSLOBinding * Parse specific binding SLO endpoint. - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws XPathExpressionException */ @@ -149,12 +149,12 @@ public static Map parseXML(Document xmlDocument, String entityId /** * Get IdP Metadata Info from XML Document - * + * * @param xmlDocument * XML document that contains IdP metadata * @param entityId * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws XPathException */ @@ -164,10 +164,10 @@ public static Map parseXML(Document xmlDocument, String entityId /** * Get IdP Metadata Info from XML Document - * + * * @param xmlDocument * XML document that contains IdP metadata - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws XPathException */ @@ -177,7 +177,7 @@ public static Map parseXML(Document xmlDocument) throws XPathExc /** * Get IdP Metadata Info from XML file - * + * * @param xmlFileName * Filename of the XML filename that contains IdP metadata * @param entityId @@ -188,7 +188,7 @@ public static Map parseXML(Document xmlDocument) throws XPathExc * Parse specific binding SSO endpoint. * @param desiredSLOBinding * Parse specific binding SLO endpoint. - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -210,12 +210,12 @@ public static Map parseFileXML(String xmlFileName, String entity /** * Get IdP Metadata Info from XML file - * + * * @param xmlFileName * Filename of the XML filename that contains IdP metadata * @param entityId * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -225,10 +225,10 @@ public static Map parseFileXML(String xmlFileName, String entity /** * Get IdP Metadata Info from XML file - * + * * @param xmlFileName * Filename of the XML filename that contains IdP metadata - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -238,7 +238,7 @@ public static Map parseFileXML(String xmlFileName) throws Except /** * Get IdP Metadata Info from XML file - * + * * @param xmlURL * URL to the XML document that contains IdP metadata * @param entityId @@ -249,7 +249,7 @@ public static Map parseFileXML(String xmlFileName) throws Except * Parse specific binding SSO endpoint. * @param desiredSLOBinding * Parse specific binding SLO endpoint. - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -260,12 +260,12 @@ public static Map parseRemoteXML(URL xmlURL, String entityId, St /** * Get IdP Metadata Info from XML file - * + * * @param xmlURL * URL to the XML document that contains IdP metadata * @param entityId * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -275,10 +275,10 @@ public static Map parseRemoteXML(URL xmlURL, String entityId) th /** * Get IdP Metadata Info from XML file - * + * * @param xmlURL * URL to the XML document that contains IdP metadata - * + * * @return Mapped values with metadata info in Saml2Settings format * @throws Exception */ @@ -293,7 +293,7 @@ public static Map parseRemoteXML(URL xmlURL) throws Exception { * the Saml2Settings object * @param metadataInfo * mapped values with metadata info in Saml2Settings format - * + * * @return the Saml2Settings object with metadata info settings loaded */ public static Saml2Settings injectIntoSettings(Saml2Settings settings, Map metadataInfo) { diff --git a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java index 30a83184..5a11b5b8 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -28,7 +28,7 @@ import com.onelogin.saml2.util.Util; /** - * Metadata class of OneLogin's Java Toolkit. + * Metadata class of Java Toolkit. * * A class that contains methods related to the metadata of the SP */ @@ -114,7 +114,7 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { LOGGER.debug("metadata --> " + unsignedMetadataString); metadataString = unsignedMetadataString; } - + /** * Allows for an extension class to post-process the SAML metadata XML generated * for this metadata instance, in order to customize the result. @@ -122,7 +122,7 @@ public Metadata(Saml2Settings settings) throws CertificateEncodingException { * This method is invoked at construction time, after all the other fields of * this class have already been initialised. Its default implementation simply * returns the input XML as-is, with no change. - * + * * @param metadataXml * the XML produced for this metadata instance by the standard * implementation provided by {@link Metadata} diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index 9ee29465..4a2fa5eb 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -24,10 +24,10 @@ import com.onelogin.saml2.util.Util; /** - * Saml2Settings class of OneLogin's Java Toolkit. + * Saml2Settings class of Java Toolkit. * * A class that implements the settings handler - */ + */ public class Saml2Settings { /** * Private property to construct a logger for this class. @@ -37,7 +37,7 @@ public class Saml2Settings { // Toolkit settings private boolean strict = true; private boolean debug = false; - + // SP private String spEntityId = ""; private URL spAssertionConsumerServiceUrl = null; @@ -86,7 +86,7 @@ public class Saml2Settings { // Compress private boolean compressRequest = true; private boolean compressResponse = true; - + // Parsing private boolean trimNameIds = false; private boolean trimAttributeValues = false; @@ -96,7 +96,7 @@ public class Saml2Settings { private Organization organization = null; private boolean spValidationOnly = false; - + /** * @return the strict setting value */ @@ -310,7 +310,7 @@ public boolean getWantAssertionsEncrypted() { public boolean getWantNameId() { return wantNameId; } - + /** * @return the wantNameIdEncrypted setting value */ @@ -394,10 +394,10 @@ public HSM getHsm() { public boolean isDebugActive() { return this.debug; } - + /** * Set the strict setting value - * + * * @param strict * the strict to be set */ @@ -770,7 +770,7 @@ public void setRequestedAuthnContextComparison(String requestedAuthnContextCompa * Set the wantXMLValidation setting value * * @param wantXMLValidation - * the wantXMLValidation value to be set. Based on it the SP will validate SAML messages against the XML scheme + * the wantXMLValidation value to be set. Based on it the SP will validate SAML messages against the XML scheme */ public void setWantXMLValidation(boolean wantXMLValidation) { this.wantXMLValidation = wantXMLValidation; @@ -857,7 +857,7 @@ public boolean isCompressResponseEnabled() { *

* Default is false, that is Name IDs are kept intact, as the SAML * specification prescribes. - * + * * @param trimNameIds * set to true to trim parsed Name IDs, set to * false to keep them intact @@ -865,26 +865,26 @@ public boolean isCompressResponseEnabled() { public void setTrimNameIds(boolean trimNameIds) { this.trimNameIds = trimNameIds; } - + /** * Determines whether Name IDs should trimmed when extracting them from parsed * SAML messages. *

* Default is false, that is Name IDs are kept intact, as the SAML * specification prescribes. - * + * * @return true if Name IDs should be trimmed, false * otherwise */ public boolean isTrimNameIds() { return trimNameIds; } - + /** * Sets whether attribute values in parsed SAML messages should be trimmed. *

* Default is false. - * + * * @param trimAttributeValues * set to true to trim parsed attribute values, set to * false to keep them intact @@ -892,23 +892,23 @@ public boolean isTrimNameIds() { public void setTrimAttributeValues(boolean trimAttributeValues) { this.trimAttributeValues = trimAttributeValues; } - + /** * Determines whether attribute values should be trimmed when extracting them * from parsed SAML messages. *

* Default is false. - * + * * @return true if attribute values should be trimmed, * false otherwise */ public boolean isTrimAttributeValues() { return trimAttributeValues; } - + /** * Set contacts info that will be listed on the Service Provider metadata - * + * * @param contacts * the contacts to set */ @@ -928,21 +928,21 @@ protected final void setOrganization(Organization organization) { /** * Checks the settings . - * + * * @return errors found on the settings data */ public List checkSettings() { List errors = new ArrayList<>(this.checkSPSettings()); - if (!spValidationOnly) { + if (!spValidationOnly) { errors.addAll(this.checkIdPSettings()); } return errors; } - + /** * Checks the IdP settings . - * + * * @return errors found on the IdP settings data */ public List checkIdPSettings() { @@ -964,7 +964,7 @@ public List checkIdPSettings() { if (!checkIdpx509certRequired() && !checkRequired(this.getIdpCertFingerprint())) { errorMsg = "idp_cert_or_fingerprint_not_found_and_required"; errors.add(errorMsg); - LOGGER.error(errorMsg); + LOGGER.error(errorMsg); } if (!checkIdpx509certRequired() && this.getNameIdEncrypted()) { @@ -978,7 +978,7 @@ public List checkIdPSettings() { /** * Auxiliary method to check Idp certificate is configured. - * + * * @return true if the Idp Certificate settings are valid */ private boolean checkIdpx509certRequired () { @@ -1072,7 +1072,7 @@ public boolean checkSPCerts() { return (cert != null && key != null); } - + /** * Auxiliary method to check required properties. * @@ -1115,7 +1115,7 @@ public boolean getSPValidationOnly() { return this.spValidationOnly; } - + /** * Gets the SP metadata. The XML representation. * @@ -1139,22 +1139,22 @@ public String getSPMetadata() throws CertificateEncodingException { this.getSignatureAlgorithm(), this.getDigestAlgorithm() ); - } catch (Exception e) { + } catch (Exception e) { LOGGER.debug("Error executing signMetadata: " + e.getMessage(), e); } } return metadataString; } - + /** * Validates an XML SP Metadata. * * @param metadataString Metadata's XML that will be validate - * + * * @return Array The list of found errors * - * @throws Exception + * @throws Exception */ public static List validateMetadata(String metadataString) throws Exception { @@ -1194,7 +1194,7 @@ public static List validateMetadata(String metadataString) throws Except } } // TODO Validate Sign if required with Util.validateMetadataSign - + return errors; } } diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 6232044e..aec3d6ea 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -39,7 +39,7 @@ import com.onelogin.saml2.util.Util; /** - * SettingsBuilder class of OneLogin's Java Toolkit. + * SettingsBuilder class of Java Toolkit. * * A class that implements the settings builder */ @@ -81,7 +81,7 @@ public class SettingsBuilder { public final static String SP_CONTACT_SUR_NAME_PROPERTY_KEY_SUFFIX = "sur_name"; public final static String SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX = "email_address"; public final static String SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX = "telephone_number"; - + // KeyStore public final static String KEYSTORE_KEY = "onelogin.saml2.keystore.store"; public final static String KEYSTORE_ALIAS = "onelogin.saml2.keystore.alias"; @@ -127,7 +127,7 @@ public class SettingsBuilder { // Parsing public final static String PARSING_TRIM_NAME_IDS = "onelogin.saml2.parsing.trim_name_ids"; public final static String PARSING_TRIM_ATTRIBUTE_VALUES = "onelogin.saml2.parsing.trim_attribute_values"; - + // Misc @Deprecated public final static String CONTACT_TECHNICAL_GIVEN_NAME = "onelogin.saml2.contacts.technical.given_name"; @@ -163,7 +163,7 @@ public SettingsBuilder fromFile(String propFileName) throws Error, IOException { * * @param propFileName OneLogin_Saml2_Settings * @param keyStoreSetting KeyStore which have the Private/Public keys - * + * * @return the SettingsBuilder object with the settings loaded from the file * * @throws IOException @@ -254,9 +254,9 @@ public Saml2Settings build() { /** * Builds the Saml2Settings object. Read the Properties object and set all the * SAML settings - * + * * @param saml2Setting an existing Saml2Settings - * + * * @return the Saml2Settings object with all the SAML settings loaded * */ @@ -485,14 +485,14 @@ private Organization loadOrganization() { /** * Loads the contacts settings from the properties file - * + * * @return a list containing all the loaded contacts */ @SuppressWarnings("deprecation") private List loadContacts() { // first split properties into a map of properties // key = contact index; value = contact properties - final SortedMap> contactProps = + final SortedMap> contactProps = extractIndexedProperties(SP_CONTACT_PROPERTY_KEY_PREFIX, samlData); // then build each contact // multiple indexed services specified @@ -521,7 +521,7 @@ private List loadContacts() { /** * Loads a single contact from settings. - * + * * @param contactProps * a map containing the contact settings * @param index @@ -552,39 +552,39 @@ private Contact loadContact(Map contactProps, int index) { *

* For instance, if the prefix is foo, all the following properties * will be extracted: - * + * *

 	 * foo[0].prop1=<value1>
 	 * foo[0].prop2=<value2>
 	 * foo[1].prop1=<value3>
 	 * 
- * + * * and the returned map will be: - * + * *
 	 * 0 => prop1=<value1>
 	 *      prop2=<value2>
 	 * 1 => prop1=<value3>
 	 * 
- * + * * The index is optional: if missing, "-1" is returned. In other words, in the * above example: - * + * *
 	 * foo.prop1=<value1>
 	 * foo.prop2=<value2>
 	 * 
- * + * * will be mapped to: - * + * *
 	 * -1 => prop1=<value1>
 	 *       prop2=<value2>
 	 * 
- * + * * Indices can be made of maximum 9 digits, to prevent overflows. Leading zeroes * are discarded. - * + * * @param prefix * the prefix that identifies the indexed property to extract * @param data @@ -592,7 +592,7 @@ private Contact loadContact(Map contactProps, int index) { * @return a map with extracted data for each identified index */ private SortedMap> extractIndexedProperties(String prefix, Map data) { - final Pattern p = Pattern.compile(Pattern.quote(prefix) + + final Pattern p = Pattern.compile(Pattern.quote(prefix) + "(?:\\[(\\d{1,9})\\])?\\.(.+)"); final SortedMap> indexedProps = new TreeMap<>(); for(final Entry prop: data.entrySet()) { @@ -611,46 +611,46 @@ private SortedMap> extractIndexedProperties(String } return indexedProps; } - + /** * Given a map containing settings data, extracts all the indexed values * identified by a given prefix. The returned map has indexes as keys and the - * corresponding values as values. Keys are sorted by their natural order + * corresponding values as values. Keys are sorted by their natural order * (i.e. iterating over the map will return entries in index order). *

* For instance, if the prefix is foo, all the following values * will be extracted: - * + * *

 	 * foo[0]=<value1>
 	 * foo[1]=<value2>
 	 * foo[2]=<value3>
 	 * 
- * + * * and the returned map will be: - * + * *
 	 * 0 => <value1>
 	 * 1 => <value2>
 	 * 3 => <value3>
 	 * 
- * + * * The index is optional: if missing, "-1" is returned. In other words, in the * above example: - * + * *
 	 * foo=<value1>
 	 * 
- * + * * will be mapped to: - * + * *
 	 * -1 => <value1>
 	 * 
- * + * * Indices can be made of maximum 9 digits, to prevent overflows. Leading zeroes * are discarded. - * + * * @param prefix * the prefix that identifies the indexed property to extract * @param data @@ -658,7 +658,7 @@ private SortedMap> extractIndexedProperties(String * @return a map with extracted values for each identified index */ private SortedMap extractIndexedValues(String prefix, Map data) { - final Pattern p = Pattern.compile(Pattern.quote(prefix) + + final Pattern p = Pattern.compile(Pattern.quote(prefix) + "(?:\\[(\\d{1,9})\\])?"); final SortedMap indexedValues = new TreeMap<>(); for(final Entry prop: data.entrySet()) { @@ -671,13 +671,13 @@ private SortedMap extractIndexedValues(String prefix, Map data) } return null; } - + /** * Loads a property of the type Boolean from the Properties object * @@ -976,7 +976,7 @@ protected X509Certificate loadCertificateFromFile(String filename) { LOGGER.error("Error loading certificate from file.", e); return null; } - + try { return Util.loadCert(certString); } catch (CertificateException e) { diff --git a/core/src/main/java/com/onelogin/saml2/util/Constants.java b/core/src/main/java/com/onelogin/saml2/util/Constants.java index d3aae65b..1de7c455 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Constants.java +++ b/core/src/main/java/com/onelogin/saml2/util/Constants.java @@ -1,17 +1,17 @@ package com.onelogin.saml2.util; /** - * Constants class of OneLogin's Java Toolkit. + * Constants class of Java Toolkit. * * A class that contains several constants related to the SAML protocol - */ + */ public final class Constants { /** * Value added to the current time in time condition validations. */ public static final Integer ALOWED_CLOCK_DRIFT = 180; // 3 min in seconds - // NameID Formats + // NameID Formats public static final String NAMEID_EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; public static final String NAMEID_X509_SUBJECT_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"; public static final String NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"; @@ -21,7 +21,7 @@ public final class Constants { public static final String NAMEID_TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"; public static final String NAMEID_PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; public static final String NAMEID_ENCRYPTED = "urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"; - + // Attribute Name Formats public static final String ATTRNAME_FORMAT_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"; public static final String ATTRNAME_FORMAT_URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"; @@ -97,8 +97,8 @@ public final class Constants { public static final String C14N11_WC = "http://www.w3.org/2006/12/xml-c14n11#WithComments"; public static final String C14NEXC = "http://www.w3.org/2001/10/xml-exc-c14n#"; public static final String C14NEXC_WC = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; - - // Sign & Crypt + + // Sign & Crypt // https://www.w3.org/TR/xmlenc-core/#sec-Alg-MessageDigest // https://www.w3.org/TR/xmlsec-algorithms/#signature-method-uris // https://tools.ietf.org/html/rfc6931 @@ -112,7 +112,7 @@ public final class Constants { public static final String RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; public static final String RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"; public static final String RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; - + public static final String TRIPLEDES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; public static final String AES128_CBC = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"; public static final String AES192_CBC = "http://www.w3.org/2001/04/xmlenc#aes192-cbc"; @@ -122,11 +122,11 @@ public final class Constants { public static final String A256KW = "http://www.w3.org/2001/04/xmlenc#kw-aes256"; public static final String RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; public static final String RSA_OAEP_MGF1P = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"; - + public static final String ENVSIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; - + private Constants() { //not called } - + } diff --git a/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java b/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java index 647a0702..3cd4f46a 100644 --- a/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java +++ b/core/src/main/java/com/onelogin/saml2/util/SchemaFactory.java @@ -16,12 +16,12 @@ import org.xml.sax.SAXException; /** - * SchemaFactory class of OneLogin's Java Toolkit. + * SchemaFactory class of Java Toolkit. * - * A class that read SAML schemas that will be used to validate XMLs of the OneLogin's Java Toolkit - */ + * A class that read SAML schemas that will be used to validate XMLs of the Java Toolkit + */ public abstract class SchemaFactory { - + /** * Private property to construct a logger for this class. */ @@ -30,7 +30,7 @@ public abstract class SchemaFactory { private SchemaFactory() { //not called } - + public static final URL SAML_SCHEMA_METADATA_2_0 = SchemaFactory.class .getResource("/schemas/saml-schema-metadata-2.0.xsd"); public static final URL SAML_SCHEMA_PROTOCOL_2_0 = SchemaFactory.class 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 8a6be158..8aa2cbe4 100644 --- a/core/src/main/java/com/onelogin/saml2/util/Util.java +++ b/core/src/main/java/com/onelogin/saml2/util/Util.java @@ -99,7 +99,7 @@ /** - * Util class of OneLogin's Java Toolkit. + * Util class of Java Toolkit. * * A class that contains several auxiliary methods related to the SAML protocol */ @@ -1127,7 +1127,7 @@ public static Boolean mustRejectDeprecatedSignatureAlgo(String signAlg, Boolean } return false; } - + /** * Validate signature of the Node. * @@ -1979,11 +1979,11 @@ public static DateTime parseDateTime(String dateTime) { } return parsedData; } - + /** * Escape a text so that it can be safely used within an XML element contents or attribute value. - * - * @param text + * + * @param text * the text to escape * @return the escaped text (null if the input is null) */ diff --git a/core/src/test/resources/config/config.adfs.properties b/core/src/test/resources/config/config.adfs.properties index 8bd0072c..3548959e 100644 --- a/core/src/test/resources/config/config.adfs.properties +++ b/core/src/test/resources/config/config.adfs.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://adfs.vc.example.com/adfs/services/trust # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://adfs.vc.example.com/adfs/ls -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -58,7 +58,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://adfs.vc.example.com/adfs/ls # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -92,7 +92,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -132,7 +132,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -140,4 +140,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index fcdbf242..b2004150 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -38,7 +38,7 @@ onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADAN # To be used during SP Key roll over onelogin.saml2.sp.x509certNew = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -50,8 +50,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -65,7 +65,7 @@ onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml onelogin.saml2.idp.single_logout_service.response.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutServiceResponse.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -101,7 +101,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -141,13 +141,13 @@ onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha5 # SAML specification states that no trimming for string elements should be performed, so no trimming will be # performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add # undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). -# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and # attribute values. onelogin.saml2.parsing.trim_name_ids = false onelogin.saml2.parsing.trim_attribute_values = false # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.organization.lang = en diff --git a/core/src/test/resources/config/config.all_specialchars.properties b/core/src/test/resources/config/config.all_specialchars.properties index 923297e0..13ede911 100644 --- a/core/src/test/resources/config/config.all_specialchars.properties +++ b/core/src/test/resources/config/config.all_specialchars.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp?a=1&b=2 # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp?a=1&b=2 # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -38,7 +38,7 @@ onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADAN # To be used during SP Key roll over onelogin.saml2.sp.x509certNew = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -50,8 +50,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -65,7 +65,7 @@ onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml onelogin.saml2.idp.single_logout_service.response.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutServiceResponse.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -101,7 +101,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -138,7 +138,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = S&P Java +onelogin.saml2.organization.name = S&P Java onelogin.saml2.organization.displayname = S&P Java "Example" onelogin.saml2.organization.url = http://sp.example.com?a=1&b=2 onelogin.saml2.organization.lang = en diff --git a/core/src/test/resources/config/config.allowduplicatednames.properties b/core/src/test/resources/config/config.allowduplicatednames.properties index fb5af81c..e21a60e9 100644 --- a/core/src/test/resources/config/config.allowduplicatednames.properties +++ b/core/src/test/resources/config/config.allowduplicatednames.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -37,7 +37,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9LcGorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6wsKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786UlujTvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXBBgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6RnfdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXzVYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRXEouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKCUn0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x9+QU/vJr7lMLzdoy-----END PRIVATE KEY----- @@ -49,8 +49,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -59,7 +59,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -93,7 +93,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -135,7 +135,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -143,4 +143,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.certfile.properties b/core/src/test/resources/config/config.certfile.properties index 97b63c38..1f0feaa9 100644 --- a/core/src/test/resources/config/config.certfile.properties +++ b/core/src/test/resources/config/config.certfile.properties @@ -12,7 +12,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -21,7 +21,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -31,8 +31,8 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Usually x509cert and privateKey of the SP are provided by files placed at # the certs folder. But we can also provide them with the following parameters -onelogin.saml2.sp.x509cert = -onelogin.saml2.sp.privatekey = +onelogin.saml2.sp.x509cert = +onelogin.saml2.sp.privatekey = # Identity Provider Data that we want connect with our SP # Identifier of the IdP entity (must be a URI) @@ -42,8 +42,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -52,17 +52,17 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP onelogin.saml2.idp.x509cert = certs/certificate1 -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha1 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example -onelogin.saml2.organization.url = http://sp.example.com \ No newline at end of file +onelogin.saml2.organization.url = http://sp.example.com diff --git a/core/src/test/resources/config/config.certstring.properties b/core/src/test/resources/config/config.certstring.properties index 7bb65492..51a8d80e 100644 --- a/core/src/test/resources/config/config.certstring.properties +++ b/core/src/test/resources/config/config.certstring.properties @@ -13,7 +13,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -22,7 +22,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -35,7 +35,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -57,16 +57,16 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==\n-----END CERTIFICATE----- -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha1 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example -onelogin.saml2.organization.url = http://sp.example.com \ No newline at end of file +onelogin.saml2.organization.url = http://sp.example.com diff --git a/core/src/test/resources/config/config.different.properties b/core/src/test/resources/config/config.different.properties index 75f2cda2..23292c67 100644 --- a/core/src/test/resources/config/config.different.properties +++ b/core/src/test/resources/config/config.different.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -36,7 +36,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -63,7 +63,7 @@ onelogin.saml2.idp.single_logout_service.url = invalid_slo_url onelogin.saml2.idp.single_logout_service.response.url = invalid_slo_response_url # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -98,7 +98,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -138,7 +138,7 @@ onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha5 # SAML specification states that no trimming for string elements should be performed, so no trimming will be # performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add # undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). -# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and # attribute values. onelogin.saml2.parsing.trim_name_ids = true onelogin.saml2.parsing.trim_attribute_values = true @@ -149,4 +149,4 @@ onelogin.saml2.organization.name = SP Java # Contacts onelogin.saml2.contacts.technical.given_name = Technical Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.invalidcontacts.properties b/core/src/test/resources/config/config.invalidcontacts.properties index 66aec8ce..064d9c33 100644 --- a/core/src/test/resources/config/config.invalidcontacts.properties +++ b/core/src/test/resources/config/config.invalidcontacts.properties @@ -13,7 +13,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -22,7 +22,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -44,8 +44,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -54,22 +54,22 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==-----END CERTIFICATE----- -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha1 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com # Contacts onelogin.saml2.contacts.technical.email_address = technical@example.com -onelogin.saml2.contacts.support.given_name = Support Guy \ No newline at end of file +onelogin.saml2.contacts.support.given_name = Support Guy diff --git a/core/src/test/resources/config/config.invalidspcertstring.properties b/core/src/test/resources/config/config.invalidspcertstring.properties index 1974e800..2216ce10 100644 --- a/core/src/test/resources/config/config.invalidspcertstring.properties +++ b/core/src/test/resources/config/config.invalidspcertstring.properties @@ -13,7 +13,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -22,7 +22,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -44,8 +44,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -54,16 +54,16 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==-----END CERTIFICATE----- -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha1 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example -onelogin.saml2.organization.url = http://sp.example.com \ No newline at end of file +onelogin.saml2.organization.url = http://sp.example.com diff --git a/core/src/test/resources/config/config.knownIdpPrivateKey.properties b/core/src/test/resources/config/config.knownIdpPrivateKey.properties index f17fdce9..a22463ea 100644 --- a/core/src/test/resources/config/config.knownIdpPrivateKey.properties +++ b/core/src/test/resources/config/config.knownIdpPrivateKey.properties @@ -21,7 +21,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -30,7 +30,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -55,8 +55,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -65,7 +65,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -147,4 +147,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.my.properties b/core/src/test/resources/config/config.my.properties index b4a43464..9a83772b 100644 --- a/core/src/test/resources/config/config.my.properties +++ b/core/src/test/resources/config/config.my.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -36,7 +36,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9LcGorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6wsKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786UlujTvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXBBgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6RnfdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXzVYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRXEouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKCUn0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x9+QU/vJr7lMLzdoy-----END PRIVATE KEY----- @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -58,7 +58,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -92,7 +92,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -132,7 +132,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -140,4 +140,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.mywithmulticert.properties b/core/src/test/resources/config/config.mywithmulticert.properties index 8218a617..e51b8074 100644 --- a/core/src/test/resources/config/config.mywithmulticert.properties +++ b/core/src/test/resources/config/config.mywithmulticert.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -36,7 +36,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOK9uFHs/nXrH9LcGorG6lB7Qs42iWK6mIE56wI7dIdsOuXf6r0ht+d+YTTis24xw+wjEHXrVN0Okh6wsKftzxo8chIo60+UB5NlKdvxAC7tpGNmrf49us/m5bdNx8IY+0pPK0c6B786UlujTvx1WFdDXh3UQPBclbWtFe5S3gLxAgMBAAECgYAPj9ngtZVZXoPWowinUbOvRmZ1ZMTVI91nsSPyCUacLM92C4I+7NuEZeYiDRUnkP7TbCyrCzXN3jwlIxdczzORhlXBBgg9Sw2fkV61CnDEMgw+aEeD5A0GDA6eTwkrawiOMs8vupjsi2/stPsa+bmpI6RnfdEKBdyDP6iQQhAxiQJBAPNtM7IMvRzlZBXoDaTTpP9rN2FR0ZcX0LT5aRZJ81qi+ZOBFeHUb6MyWvzZKfPinj9JO3s/9e3JbMXemRWBmvcCQQDuc+NfAeW200QyjoC3Ed3jueLMrY1Q3zTcSUhRPw/0pIKgRGZJerro8N6QY2JziV2mxK855gKTwwBigMHL2S9XAkEAwuBfjGDqXOG/uFHn6laNNvWshjqsIdus99Tbrj5RlfP2/YFP9VTOcsXzVYy9K0P3EA8ekVLpHQ4uCFJmF3OEjQJBAMvwO69/HOufhv1CWZ25XzAsRGhPqsRXEouw9XPfXpMavEm8FkuT9xXRJFkTVxl/i6RdJYx8Rwn/Rm34t0bUKqMCQQCrAtKCUn0PLcemAzPi8ADJlbMDG/IDXNbSej0Y4tw9Cdho1Q38XLZJi0RNdNvQJD1fWu3x9+QU/vJr7lMLzdoy-----END PRIVATE KEY----- @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -58,7 +58,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -94,7 +94,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -134,7 +134,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -142,4 +142,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.mywithnocert.properties b/core/src/test/resources/config/config.mywithnocert.properties index d737de60..ef1d5df6 100644 --- a/core/src/test/resources/config/config.mywithnocert.properties +++ b/core/src/test/resources/config/config.mywithnocert.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -35,7 +35,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe # the certs folder. But we can also provide them with the following parameters onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = https://pitbulk.no-ip.org/simplesaml/saml2/idp/met # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -57,7 +57,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = https://pitbulk.no-ip.org/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -92,7 +92,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -129,7 +129,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -137,4 +137,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.newattack.properties b/core/src/test/resources/config/config.newattack.properties index 3255de68..a7d8b83a 100644 --- a/core/src/test/resources/config/config.newattack.properties +++ b/core/src/test/resources/config/config.newattack.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -35,7 +35,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe # the certs folder. But we can also provide them with the following parameters onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpAl0+mhy6MPdyjyA6G14jrDWS65ysLchK4t/vwpEDz0SQlEoG1kMzllSm7zZS3XregA7DjNaUYQqwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALM2vGCiQ/vm+a6v40+VX2zdqHA2Q/1vF1ibQzJ54MJCOVWvs+vQXfZFhdm0OPM2IrDU7oqvKPqP6xOAeJK6H0yP7M4YL3fatSvIYmmfyXC9kt3Svz/NyrHzPhUnJ0ye/sUSXxnzQxwcm/9PwAqrQaA3QpQkH57ybF/OoryPe+2h-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem #onelogin.saml2.sp.privatekey = -----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpAl0+mhy6MPdyjyA6G14jrDWS65ysLchK4t/vwpEDz0SQlEoG1kMzllSm7zZS3XregA7DjNaUYQqwIDAQABAoGBALGR6bRBit+yV5TUU3MZSrf8WQSLWDLgs/33FQSAEYSib4+DJke2lKbI6jkGUoSJgFUXFbaQLtMY2+3VDsMKPBdAge9gIdvbkC4yoKjLGm/FBDOxxZcfLpR+9OPqU3qM9D0CNuliBWI7Je+p/zs09HIYucpDXy9E18KA1KNF6rfhAkEA9KoNam6wAKnmvMzz31ws3RuIOUeo2rx6aaVY95+P9tTxd6U+pNkwxy1aCGP+InVSwlYNA1aQ4Axi/GdMIWMkxwJBAPO1CP7cQNZQmu7yusY+GUObDII5YK9WLaY4RAicn5378crPBFxvUkqf9G6FHo7u88iTCIp+vwa3Hn9Tumg3iP0CQQDgUXWBasCVqzCxU5wY4tMDWjXYhpoLCpmVeRML3dDJt004rFm2HKe7Rhpw7PTZNQZOxUSjFeA4e0LaNf838UWLAkB8QfbHM3ffjhOg96PhhjINdVWoZCb230LBOHj/xxPfUmFTHcBEfQIBSJMxcrBFAnLL9qPpMXymqOFk3ETz9DTlAj8E0qGbp78aVbTOtuwEwNJII+RPw+Zkc+lKR+yaWkAzfIXw527NPHH3+rnBG72wyZr9ud4LAum9jh+5No1LQpk=-----END RSA PRIVATE KEY----- onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOjqb5Bm9hD/EvQSUuC6krVANxgvh/k9yTCw2BL2lS9Xt/NOoIeTxOEvFOTjYxDYxwmw55QlU/vbwqHKaMUKrGA/hW7BGT2ukCXT6aHLow93KPIDobXiOsNZLrnKwtyEri3+/CkQPPRJCUSgbWQzOWVKbvNlLdet6ADsOM1pRhCrAgMBAAECgYEAsZHptEGK37JXlNRTcxlKt/xZBItYMuCz/fcVBIARhKJvj4MmR7aUpsjqOQZShImAVRcVtpAu0xjb7dUOwwo8F0CB72Ah29uQLjKgqMsab8UEM7HFlx8ulH704+pTeoz0PQI26WIFYjsl76n/OzT0chi5ykNfL0TXwoDUo0Xqt+ECQQD0qg1qbrAAqea8zPPfXCzdG4g5R6javHpppVj3n4/21PF3pT6k2TDHLVoIY/4idVLCVg0DVpDgDGL8Z0whYyTHAkEA87UI/txA1lCa7vK6xj4ZQ5sMgjlgr1YtpjhECJyfnfvxys8EXG9SSp/0boUeju7zyJMIin6/Brcef1O6aDeI/QJBAOBRdYFqwJWrMLFTnBji0wNaNdiGmgsKmZV5Ewvd0Mm3TTisWbYcp7tGGnDs9Nk1Bk7FRKMV4Dh7Qto1/zfxRYsCQHxB9sczd9+OE6D3o+GGMg11VahkJvbfQsE4eP/HE99SYVMdwER9AgFIkzFysEUCcsv2o+kxfKao4WTcRPP0NOUCPwTSoZunvxpVtM627ATA0kgj5E/D5mRz6UpH7JpaQDN8hfDnbs08cff6ucEbvbDJmv253gsC6b2OH7k2jUtCmQ==-----END PRIVATE KEY----- @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -58,12 +58,12 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP -#onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIICPjCCAaegAwIBAgIBADANBgkqhkiG9w0BAQ0FADA8MQswCQYDVQQGEwJ1czEPMA0GA1UECAwGSGF3YWlpMQswCQYDVQQKDAJNZTEPMA0GA1UEAwwGbWUuY29tMB4XDTE2MDYxNDE0MDIxM1oXDTE3MDYxNDE0MDIxM1owPDELMAkGA1UEBhMCdXMxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UECgwCTWUxDzANBgNVBAMMBm1lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5xWsnAmIgCwkwbQodP4eiLAUOPmuurlV29whcGt6Ac3hvOQtnMm9gdlNJrvjlv4ZaG3H6A0Akys811Amdm+oKveXymFoLG4KHLMjTMIfQvKOl8Id/+Uvx69Zdw/0ouemhIagpw1z/bOXzL/i/3KxGJg8nwaV3dxtbPNSFzcDvy0CAwEAAaNQME4wHQYDVR0OBBYEFE4gjnKB5yJGWZMcu5lHlRnSAae2MB8GA1UdIwQYMBaAFE4gjnKB5yJGWZMcu5lHlRnSAae2MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAOsmRx6tknVDHC8E+Eas2eF6O4Hm15Yt5XAjzIX3OiE2zvqm3fOk3HNjcHOAIFB7Mdvr6+23ARXpZFKiS2+MkUs5wmEzCLqU/hROyjyj9PYG1jMPrAHPOpWjVtlWuJslN28I6ziM8uq+uitTjIdt8JZ6P2dWtoTmDgsVUmFM0naU=-----END CERTIFICATE----- +#onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIICPjCCAaegAwIBAgIBADANBgkqhkiG9w0BAQ0FADA8MQswCQYDVQQGEwJ1czEPMA0GA1UECAwGSGF3YWlpMQswCQYDVQQKDAJNZTEPMA0GA1UEAwwGbWUuY29tMB4XDTE2MDYxNDE0MDIxM1oXDTE3MDYxNDE0MDIxM1owPDELMAkGA1UEBhMCdXMxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UECgwCTWUxDzANBgNVBAMMBm1lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5xWsnAmIgCwkwbQodP4eiLAUOPmuurlV29whcGt6Ac3hvOQtnMm9gdlNJrvjlv4ZaG3H6A0Akys811Amdm+oKveXymFoLG4KHLMjTMIfQvKOl8Id/+Uvx69Zdw/0ouemhIagpw1z/bOXzL/i/3KxGJg8nwaV3dxtbPNSFzcDvy0CAwEAAaNQME4wHQYDVR0OBBYEFE4gjnKB5yJGWZMcu5lHlRnSAae2MB8GA1UdIwQYMBaAFE4gjnKB5yJGWZMcu5lHlRnSAae2MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAOsmRx6tknVDHC8E+Eas2eF6O4Hm15Yt5XAjzIX3OiE2zvqm3fOk3HNjcHOAIFB7Mdvr6+23ARXpZFKiS2+MkUs5wmEzCLqU/hROyjyj9PYG1jMPrAHPOpWjVtlWuJslN28I6ziM8uq+uitTjIdt8JZ6P2dWtoTmDgsVUmFM0naU=-----END CERTIFICATE----- onelogin.saml2.idp.certfingerprint = 385b1eec71143f00db6af936e2ea12a28771d72c onelogin.saml2.idp.certfingerprint_algorithm = sha1 @@ -94,7 +94,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -131,7 +131,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -139,4 +139,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.newattack2.properties b/core/src/test/resources/config/config.newattack2.properties index 80a5d8f6..5f08a1c9 100644 --- a/core/src/test/resources/config/config.newattack2.properties +++ b/core/src/test/resources/config/config.newattack2.properties @@ -14,13 +14,13 @@ onelogin.saml2.sp.entityid = example.com # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = https://example.com/endpoint # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -32,7 +32,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe # the certs folder. But we can also provide them with the following parameters onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpAl0+mhy6MPdyjyA6G14jrDWS65ysLchK4t/vwpEDz0SQlEoG1kMzllSm7zZS3XregA7DjNaUYQqwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALM2vGCiQ/vm+a6v40+VX2zdqHA2Q/1vF1ibQzJ54MJCOVWvs+vQXfZFhdm0OPM2IrDU7oqvKPqP6xOAeJK6H0yP7M4YL3fatSvIYmmfyXC9kt3Svz/NyrHzPhUnJ0ye/sUSXxnzQxwcm/9PwAqrQaA3QpQkH57ybF/OoryPe+2h-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem #onelogin.saml2.sp.privatekey = -----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpAl0+mhy6MPdyjyA6G14jrDWS65ysLchK4t/vwpEDz0SQlEoG1kMzllSm7zZS3XregA7DjNaUYQqwIDAQABAoGBALGR6bRBit+yV5TUU3MZSrf8WQSLWDLgs/33FQSAEYSib4+DJke2lKbI6jkGUoSJgFUXFbaQLtMY2+3VDsMKPBdAge9gIdvbkC4yoKjLGm/FBDOxxZcfLpR+9OPqU3qM9D0CNuliBWI7Je+p/zs09HIYucpDXy9E18KA1KNF6rfhAkEA9KoNam6wAKnmvMzz31ws3RuIOUeo2rx6aaVY95+P9tTxd6U+pNkwxy1aCGP+InVSwlYNA1aQ4Axi/GdMIWMkxwJBAPO1CP7cQNZQmu7yusY+GUObDII5YK9WLaY4RAicn5378crPBFxvUkqf9G6FHo7u88iTCIp+vwa3Hn9Tumg3iP0CQQDgUXWBasCVqzCxU5wY4tMDWjXYhpoLCpmVeRML3dDJt004rFm2HKe7Rhpw7PTZNQZOxUSjFeA4e0LaNf838UWLAkB8QfbHM3ffjhOg96PhhjINdVWoZCb230LBOHj/xxPfUmFTHcBEfQIBSJMxcrBFAnLL9qPpMXymqOFk3ETz9DTlAj8E0qGbp78aVbTOtuwEwNJII+RPw+Zkc+lKR+yaWkAzfIXw527NPHH3+rnBG72wyZr9ud4LAum9jh+5No1LQpk=-----END RSA PRIVATE KEY----- onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOjqb5Bm9hD/EvQSUuC6krVANxgvh/k9yTCw2BL2lS9Xt/NOoIeTxOEvFOTjYxDYxwmw55QlU/vbwqHKaMUKrGA/hW7BGT2ukCXT6aHLow93KPIDobXiOsNZLrnKwtyEri3+/CkQPPRJCUSgbWQzOWVKbvNlLdet6ADsOM1pRhCrAgMBAAECgYEAsZHptEGK37JXlNRTcxlKt/xZBItYMuCz/fcVBIARhKJvj4MmR7aUpsjqOQZShImAVRcVtpAu0xjb7dUOwwo8F0CB72Ah29uQLjKgqMsab8UEM7HFlx8ulH704+pTeoz0PQI26WIFYjsl76n/OzT0chi5ykNfL0TXwoDUo0Xqt+ECQQD0qg1qbrAAqea8zPPfXCzdG4g5R6javHpppVj3n4/21PF3pT6k2TDHLVoIY/4idVLCVg0DVpDgDGL8Z0whYyTHAkEA87UI/txA1lCa7vK6xj4ZQ5sMgjlgr1YtpjhECJyfnfvxys8EXG9SSp/0boUeju7zyJMIin6/Brcef1O6aDeI/QJBAOBRdYFqwJWrMLFTnBji0wNaNdiGmgsKmZV5Ewvd0Mm3TTisWbYcp7tGGnDs9Nk1Bk7FRKMV4Dh7Qto1/zfxRYsCQHxB9sczd9+OE6D3o+GGMg11VahkJvbfQsE4eP/HE99SYVMdwER9AgFIkzFysEUCcsv2o+kxfKao4WTcRPP0NOUCPwTSoZunvxpVtM627ATA0kgj5E/D5mRz6UpH7JpaQDN8hfDnbs08cff6ucEbvbDJmv253gsC6b2OH7k2jUtCmQ==-----END PRIVATE KEY----- @@ -45,8 +45,8 @@ onelogin.saml2.idp.entityid = idp.example.com # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/saml/sso -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -55,12 +55,12 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/saml/slo # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Public x509 certificate of the IdP -#onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIICPjCCAaegAwIBAgIBADANBgkqhkiG9w0BAQ0FADA8MQswCQYDVQQGEwJ1czEPMA0GA1UECAwGSGF3YWlpMQswCQYDVQQKDAJNZTEPMA0GA1UEAwwGbWUuY29tMB4XDTE2MDYxNDE0MDIxM1oXDTE3MDYxNDE0MDIxM1owPDELMAkGA1UEBhMCdXMxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UECgwCTWUxDzANBgNVBAMMBm1lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5xWsnAmIgCwkwbQodP4eiLAUOPmuurlV29whcGt6Ac3hvOQtnMm9gdlNJrvjlv4ZaG3H6A0Akys811Amdm+oKveXymFoLG4KHLMjTMIfQvKOl8Id/+Uvx69Zdw/0ouemhIagpw1z/bOXzL/i/3KxGJg8nwaV3dxtbPNSFzcDvy0CAwEAAaNQME4wHQYDVR0OBBYEFE4gjnKB5yJGWZMcu5lHlRnSAae2MB8GA1UdIwQYMBaAFE4gjnKB5yJGWZMcu5lHlRnSAae2MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAOsmRx6tknVDHC8E+Eas2eF6O4Hm15Yt5XAjzIX3OiE2zvqm3fOk3HNjcHOAIFB7Mdvr6+23ARXpZFKiS2+MkUs5wmEzCLqU/hROyjyj9PYG1jMPrAHPOpWjVtlWuJslN28I6ziM8uq+uitTjIdt8JZ6P2dWtoTmDgsVUmFM0naU=-----END CERTIFICATE----- +#onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIICPjCCAaegAwIBAgIBADANBgkqhkiG9w0BAQ0FADA8MQswCQYDVQQGEwJ1czEPMA0GA1UECAwGSGF3YWlpMQswCQYDVQQKDAJNZTEPMA0GA1UEAwwGbWUuY29tMB4XDTE2MDYxNDE0MDIxM1oXDTE3MDYxNDE0MDIxM1owPDELMAkGA1UEBhMCdXMxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UECgwCTWUxDzANBgNVBAMMBm1lLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5xWsnAmIgCwkwbQodP4eiLAUOPmuurlV29whcGt6Ac3hvOQtnMm9gdlNJrvjlv4ZaG3H6A0Akys811Amdm+oKveXymFoLG4KHLMjTMIfQvKOl8Id/+Uvx69Zdw/0ouemhIagpw1z/bOXzL/i/3KxGJg8nwaV3dxtbPNSFzcDvy0CAwEAAaNQME4wHQYDVR0OBBYEFE4gjnKB5yJGWZMcu5lHlRnSAae2MB8GA1UdIwQYMBaAFE4gjnKB5yJGWZMcu5lHlRnSAae2MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAOsmRx6tknVDHC8E+Eas2eF6O4Hm15Yt5XAjzIX3OiE2zvqm3fOk3HNjcHOAIFB7Mdvr6+23ARXpZFKiS2+MkUs5wmEzCLqU/hROyjyj9PYG1jMPrAHPOpWjVtlWuJslN28I6ziM8uq+uitTjIdt8JZ6P2dWtoTmDgsVUmFM0naU=-----END CERTIFICATE----- onelogin.saml2.idp.certfingerprint = 4b68c453c7d994aad9025c99d5efcf566287fe8d onelogin.saml2.idp.certfingerprint_algorithm = sha1 @@ -91,7 +91,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -128,7 +128,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -136,4 +136,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.samecerts.properties b/core/src/test/resources/config/config.samecerts.properties index 9d94a4d3..bae8d2f4 100644 --- a/core/src/test/resources/config/config.samecerts.properties +++ b/core/src/test/resources/config/config.samecerts.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -35,7 +35,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe # the certs folder. But we can also provide them with the following parameters onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- @@ -47,8 +47,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -57,7 +57,7 @@ onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0: onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -91,7 +91,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -128,7 +128,7 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com @@ -136,4 +136,4 @@ onelogin.saml2.organization.url = http://sp.example.com onelogin.saml2.contacts.technical.given_name = Technical Guy onelogin.saml2.contacts.technical.email_address = technical@example.com onelogin.saml2.contacts.support.given_name = Support Guy -onelogin.saml2.contacts.support.email_address = support@example.com \ No newline at end of file +onelogin.saml2.contacts.support.email_address = support@example.com diff --git a/core/src/test/resources/config/config.somevaluesempty.properties b/core/src/test/resources/config/config.somevaluesempty.properties index 60d87e8f..7d32326a 100644 --- a/core/src/test/resources/config/config.somevaluesempty.properties +++ b/core/src/test/resources/config/config.somevaluesempty.properties @@ -14,7 +14,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata. # URL Location where the from the IdP will be returned onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -23,7 +23,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp # SAML protocol binding for the Single Logout Service of the SP. -# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +# SAMLToolkit supports for this endpoint the HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect # Specifies constraints on the name identifier to be used to @@ -36,7 +36,7 @@ onelogin.saml2.sp.nameidformat = onelogin.saml2.sp.x509cert = -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = @@ -48,8 +48,8 @@ onelogin.saml2.idp.entityid = http://idp.example.com/ # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -63,7 +63,7 @@ onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml onelogin.saml2.idp.single_logout_service.response.url = # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -98,7 +98,7 @@ onelogin.saml2.security.want_messages_signed = true onelogin.saml2.security.want_assertions_signed = true # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null/false (in order to not sign) or true (sign using SP private key) +# Right now supported null/false (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = true # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -135,7 +135,7 @@ onelogin.saml2.security.signature_algorithm = onelogin.saml2.security.digest_algorithm = # Organization -onelogin.saml2.organization.name = +onelogin.saml2.organization.name = onelogin.saml2.organization.displayname = onelogin.saml2.organization.url = @@ -143,4 +143,4 @@ onelogin.saml2.organization.url = onelogin.saml2.contacts.technical.given_name = onelogin.saml2.contacts.technical.email_address = onelogin.saml2.contacts.support.given_name = -onelogin.saml2.contacts.support.email_address = \ No newline at end of file +onelogin.saml2.contacts.support.email_address = diff --git a/pom.xml b/pom.xml index 1e4cabea..d6ebe7a8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,9 +5,9 @@ 2.9.1-SNAPSHOT pom - OneLogin java-saml Toolkit Project - A Java SAML toolkit by OneLogin - https://github.com/onelogin/java-saml + java-saml Toolkit Project + A Java SAML toolkit + https://github.com/saml-toolkit/java-saml UTF-8 @@ -154,9 +154,9 @@ - scm:git:git@github.com:onelogin/java-saml.git - scm:git:git@github.com:onelogin/java-saml.git - https://github.com/onelogin/java-saml + scm:git:git@github.com:saml-toolkit/java-saml.git + scm:git:git@github.com:saml-toolkit/java-saml.git + https://github.com/saml-toolkit/java-saml HEAD @@ -172,7 +172,7 @@ Sixto Martín García https://github.com/pitbulk - OneLogin + SAML Toolkit diff --git a/samples/java-saml-tookit-jspsample/pom.xml b/samples/java-saml-tookit-jspsample/pom.xml index 13afeb5b..d960d4f7 100644 --- a/samples/java-saml-tookit-jspsample/pom.xml +++ b/samples/java-saml-tookit-jspsample/pom.xml @@ -8,7 +8,7 @@ java-saml-tookit-jspsample war - OneLogin java-saml Toolkit Sample Webapp + java-saml Toolkit Sample Webapp diff --git a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties index ce813fd3..e7249704 100644 --- a/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties +++ b/samples/java-saml-tookit-jspsample/src/main/resources/onelogin.saml.properties @@ -19,7 +19,7 @@ onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-tookit-jspsample/me onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-tookit-jspsample/acs.jsp # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST @@ -28,7 +28,7 @@ onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2 onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-tookit-jspsample/sls.jsp # SAML protocol binding to be used when returning the or sending the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -42,7 +42,7 @@ onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspe onelogin.saml2.sp.x509cert = -# Requires Format PKCS#8 BEGIN PRIVATE KEY +# Requires Format PKCS#8 BEGIN PRIVATE KEY # If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem onelogin.saml2.sp.privatekey = @@ -56,8 +56,8 @@ onelogin.saml2.idp.entityid = # URL Target of the IdP where the SP will send the Authentication Request Message onelogin.saml2.idp.single_sign_on_service.url = -# SAML protocol binding to be used to deliver the message -# to the IdP. Onelogin Toolkit supports for this endpoint the +# SAML protocol binding to be used to deliver the message +# to the IdP. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -71,7 +71,7 @@ onelogin.saml2.idp.single_logout_service.url = onelogin.saml2.idp.single_logout_service.response.url = # SAML protocol binding to be used when returning the -# message. Onelogin Toolkit supports for this endpoint the +# message. SAMLToolkit supports for this endpoint the # HTTP-Redirect binding only onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect @@ -85,7 +85,7 @@ onelogin.saml2.idp.x509cert = # If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to # let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512 # 'sha1' is the default value. -# onelogin.saml2.idp.certfingerprint = +# onelogin.saml2.idp.certfingerprint = # onelogin.saml2.idp.certfingerprint_algorithm = sha256 @@ -116,7 +116,7 @@ onelogin.saml2.security.want_messages_signed = false onelogin.saml2.security.want_assertions_signed = false # Indicates a requirement for the Metadata of this SP to be signed. -# Right now supported null (in order to not sign) or true (sign using SP private key) +# Right now supported null (in order to not sign) or true (sign using SP private key) onelogin.saml2.security.sign_metadata = # Indicates a requirement for the Assertions received by this SP to be encrypted @@ -153,16 +153,16 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- # 'http://www.w3.org/2001/04/xmlenc#sha256' # 'http://www.w3.org/2001/04/xmldsig-more#sha384' # 'http://www.w3.org/2001/04/xmlenc#sha512' -onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256 +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256 # Reject Signatures with deprecated algorithms (sha1) onelogin.saml2.security.reject_deprecated_alg = true # Organization -onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.name = SP Java onelogin.saml2.organization.displayname = SP Java Example onelogin.saml2.organization.url = http://sp.example.com -onelogin.saml2.organization.lang = +onelogin.saml2.organization.lang = # Contacts onelogin.saml2.contacts.technical.given_name = Technical Guy diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/acs.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/acs.jsp index 99d640d8..9c8ff2e0 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/acs.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/acs.jsp @@ -2,7 +2,7 @@ <%@page import="com.onelogin.saml2.servlet.ServletUtils"%> <%@page import="java.util.Collection"%> <%@page import="java.util.List"%> -<%@page import="java.util.Map"%> +<%@page import="java.util.Map"%> <%@page import="org.apache.commons.lang3.StringUtils" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> @@ -12,7 +12,7 @@ - A Java SAML Toolkit by OneLogin demo + A Java SAML Toolkit @@ -24,9 +24,9 @@
-

A Java SAML Toolkit by OneLogin demo

+

A Java SAML Toolkit

- + <% Auth auth = new Auth(request, response); @@ -61,7 +61,7 @@ session.setAttribute("sessionIndex", sessionIndex); session.setAttribute("nameidNameQualifier", nameidNameQualifier); session.setAttribute("nameidSPNameQualifier", nameidSPNameQualifier); - + String relayState = request.getParameter("RelayState"); @@ -69,12 +69,12 @@ !relayState.contains("/dologin.jsp")) { // We don't want to be redirected to login.jsp neither response.sendRedirect(request.getParameter("RelayState")); } else { - + if (attributes.isEmpty()) { %> - <% + <% } else { %> @@ -86,7 +86,7 @@ - <% + <% Collection keys = attributes.keySet(); for(String name :keys){ out.println("" + name + ""); @@ -94,13 +94,13 @@ for(String value :values) { out.println("
  • " + value + "
  • "); } - + out.println(""); } %> - <% + <% } %> See user data stored at session diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/attrs.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/attrs.jsp index f98e2cea..65c4aced 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/attrs.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/attrs.jsp @@ -13,7 +13,7 @@ - A Java SAML Toolkit by OneLogin demo + A Java SAML Toolkit @@ -25,19 +25,19 @@
    -

    A Java SAML Toolkit by OneLogin demo

    +

    A Java SAML Toolkit

    <% Boolean found = false; @SuppressWarnings("unchecked") - Enumeration elems = (Enumeration) session.getAttributeNames(); - + Enumeration elems = (Enumeration) session.getAttributeNames(); + while (elems.hasMoreElements() && !found) { String value = (String) elems.nextElement(); if (value.equals("attributes") || value.equals("nameId")) { found = true; } } - + if (found) { String nameId = (String) session.getAttribute("nameId"); @SuppressWarnings("unchecked") @@ -46,11 +46,11 @@ if (!nameId.isEmpty()) { out.println("
    NameId: " + nameId + "
    "); } - + if (attributes.isEmpty()) { %> - <% + <% } else { %> @@ -63,7 +63,7 @@ - <% + <% Collection keys = attributes.keySet(); for(String name :keys){ out.println("" + name + ""); @@ -71,7 +71,7 @@ for(String value :values) { out.println("
  • " + value + "
  • "); } - + out.println(""); } %> diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/index.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/index.jsp index b783a4e8..9483fb2d 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/index.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/index.jsp @@ -6,7 +6,7 @@ - A Java SAML Toolkit by OneLogin demo + A Java SAML Toolkit @@ -18,7 +18,7 @@
    -

    A Java SAML Toolkit by OneLogin demo

    +

    A Java SAML Toolkit

    Login Login and access to attrs.jsp page
    diff --git a/samples/java-saml-tookit-jspsample/src/main/webapp/sls.jsp b/samples/java-saml-tookit-jspsample/src/main/webapp/sls.jsp index bf80405c..83f2ad7d 100644 --- a/samples/java-saml-tookit-jspsample/src/main/webapp/sls.jsp +++ b/samples/java-saml-tookit-jspsample/src/main/webapp/sls.jsp @@ -2,7 +2,7 @@ <%@page import="java.util.Collection"%> <%@page import="java.util.HashMap"%> <%@page import="java.util.List"%> -<%@page import="java.util.Map"%> +<%@page import="java.util.Map"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> @@ -11,7 +11,7 @@ - A Java SAML Toolkit by OneLogin demo + A Java SAML Toolkit @@ -23,14 +23,14 @@
    -

    A Java SAML Toolkit by OneLogin demo

    - Logout +

    A Java SAML Toolkit

    + Logout <% Auth auth = new Auth(request, response); auth.processSLO(); - + List errors = auth.getErrors(); - + if (errors.isEmpty()) { out.println("

    Sucessfully logged out

    "); out.println("Login"); diff --git a/samples/pom.xml b/samples/pom.xml index 799ec247..36ab644b 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -7,7 +7,7 @@ java-saml-tookit-samples - OneLogin java-saml Toolkit Samples + java-saml Toolkit Samples pom diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 47e8b5d2..37d2f787 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -7,7 +7,7 @@ jar - OneLogin java-saml Toolkit + java-saml Toolkit java-saml diff --git a/toolkit/src/main/java/com/onelogin/saml2/Auth.java b/toolkit/src/main/java/com/onelogin/saml2/Auth.java index 511914b8..6d035df9 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/Auth.java +++ b/toolkit/src/main/java/com/onelogin/saml2/Auth.java @@ -42,7 +42,7 @@ import com.onelogin.saml2.util.Util; /** - * Main class of OneLogin's Java Toolkit. + * Main class of Java Toolkit. * * This class implements the SP SAML instance. * Defines the methods that you can invoke in your application in @@ -106,7 +106,7 @@ public class Auth { * The ID of the last message processed */ private String lastMessageId; - + /** * The issue instant of the last message processed */ @@ -169,9 +169,9 @@ public class Auth { * encrypted, by default tries to return the decrypted XML */ private String lastResponse; - + private static final SamlMessageFactory DEFAULT_SAML_MESSAGE_FACTORY = new SamlMessageFactory() {}; - + private SamlMessageFactory samlMessageFactory = DEFAULT_SAML_MESSAGE_FACTORY; /** @@ -197,7 +197,7 @@ public Auth() throws IOException, SettingsException, Error { public Auth(KeyStoreSettings keyStoreSetting) throws IOException, SettingsException, Error { this("onelogin.saml.properties", keyStoreSetting); } - + /** * Initializes the SP SAML instance. * @@ -242,7 +242,7 @@ public Auth(HttpServletRequest request, HttpServletResponse response) throws IOE /** * Initializes the SP SAML instance. - * + * * @param keyStoreSetting KeyStoreSettings is a KeyStore which have the Private/Public keys * @param request HttpServletRequest object to be processed * @param response HttpServletResponse object to be used @@ -616,11 +616,11 @@ public String login(String relayState, AuthnRequestParams authnRequestParams, Bo */ public String login(String relayState, AuthnRequestParams authnRequestParams, Boolean stay, Map parameters) throws IOException, SettingsException { AuthnRequest authnRequest = samlMessageFactory.createAuthnRequest(settings, authnRequestParams); - + if (parameters == null) { parameters = new HashMap(); } - + String samlRequest = authnRequest.getEncodedAuthnRequest(); parameters.put("SAMLRequest", samlRequest); @@ -628,7 +628,7 @@ public String login(String relayState, AuthnRequestParams authnRequestParams, Bo if (relayState == null) { relayState = ServletUtils.getSelfRoutedURLNoQuery(request); } - + if (!relayState.isEmpty()) { parameters.put("RelayState", relayState); } @@ -1137,7 +1137,7 @@ public void logout(String relayState, String nameId, String sessionIndex) /** * Initiates the SLO process. - * + * * @throws IOException * @throws SettingsException */ @@ -1323,7 +1323,7 @@ public String processSLO(Boolean keepLocalSession, String requestId, Boolean sta } String inResponseTo = logoutRequest.id; - LogoutResponse logoutResponseBuilder = samlMessageFactory.createOutgoingLogoutResponse(settings, + LogoutResponse logoutResponseBuilder = samlMessageFactory.createOutgoingLogoutResponse(settings, new LogoutResponseParams(inResponseTo, Constants.STATUS_SUCCESS)); lastResponse = logoutResponseBuilder.getLogoutResponseXml(); @@ -1463,10 +1463,10 @@ public final DateTime getSessionExpiration() { public String getLastMessageId() { return lastMessageId; } - + /** * Returns the issue instant of the last message processed. - * + * * @return The issue instant of the last message processed */ public Calendar getLastMessageIssueInstant() { @@ -1516,10 +1516,10 @@ public Exception getLastValidationException() { public String getLastRequestId() { return lastRequestId; } - + /** * Returns the issue instant of the last request generated (AuthnRequest or LogoutRequest). - * + * * @return the issue instant of the last request generated (AuthnRequest or LogoutRequest), * null if none */ @@ -1593,7 +1593,7 @@ public String buildResponseSignature(String samlResponse, String relayState, Str private String buildSignature(String samlMessage, String relayState, String signAlgorithm, String type) throws SettingsException, IllegalArgumentException { String signature = ""; - + if (!settings.checkSPCerts()) { String errorMsg = "Trying to sign the " + type + " but can't load the SP private key"; LOGGER.error("buildSignature error. " + errorMsg); @@ -1601,16 +1601,16 @@ private String buildSignature(String samlMessage, String relayState, String sign } PrivateKey key = settings.getSPkey(); - + String msg = type + "=" + Util.urlEncoder(samlMessage); if (StringUtils.isNotEmpty(relayState)) { msg += "&RelayState=" + Util.urlEncoder(relayState); } - + if (StringUtils.isEmpty(signAlgorithm)) { signAlgorithm = Constants.RSA_SHA1; } - + msg += "&SigAlg=" + Util.urlEncoder(signAlgorithm); try { @@ -1656,7 +1656,7 @@ public String getLastResponseXML() { *

    * This allows consumers to provide their own extension classes for SAML message * XML generation and/or processing. - * + * * @param samlMessageFactory * the factory to use to create SAML message objects; if * null, a default provider will be used which creates diff --git a/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java b/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java index c62bdd71..1c2f7bb9 100644 --- a/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java +++ b/toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java @@ -15,7 +15,7 @@ import com.onelogin.saml2.util.Util; /** - * ServletUtils class of OneLogin's Java Toolkit. + * ServletUtils class of Java Toolkit. * * A class that contains several auxiliary methods related to HttpServletRequest and HttpServletResponse */ @@ -24,7 +24,7 @@ public class ServletUtils { private ServletUtils() { //not called } - + /** * Creates an HttpRequest from an HttpServletRequest. * @@ -148,7 +148,7 @@ public static String getSelfRoutedURLNoQuery(HttpServletRequest request) { * GET parameters to be added * @param stay * True if we want to stay (returns the url string) False to execute redirection - * + * * @return string the target URL * @throws IOException * @@ -197,7 +197,7 @@ public static String sendRedirect(HttpServletResponse response, String location, public static void sendRedirect(HttpServletResponse response, String location, Map parameters) throws IOException { sendRedirect(response, location, parameters, false); } - + /** * Redirect to location url * From 684bfe045bc3fdb0a4fde2f266dd45dcd16a0fd7 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 14:51:46 -0800 Subject: [PATCH 124/133] Update joda-time. --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index b00d34e2..b179390e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,7 +48,7 @@ joda-time joda-time - 2.10.6 + 2.10.14 From d5b713d0166c1562100b9aaf31c74571479d7cd8 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 15:03:03 -0800 Subject: [PATCH 125/133] Enable manual github actions workflow run. --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 88ce2a45..60e476d3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -3,7 +3,7 @@ name: java-saml CI with Maven -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: test: From 0e244b9a601132de7b738444edc73e34c57812db Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 15:15:21 -0800 Subject: [PATCH 126/133] Update azure-security-keyvault-keys. --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index b179390e..3ebcc721 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,7 +72,7 @@ com.azure azure-security-keyvault-keys - 4.3.4 + 4.3.8 true From 094ddc2a3b0fe864c5caa1dbdb1586d6e6a5c113 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 15:19:24 -0800 Subject: [PATCH 127/133] Update azure-security-keyvault-kyes. --- core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 3ebcc721..4e516a41 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,7 +72,7 @@ com.azure azure-security-keyvault-keys - 4.3.8 + 4.5.2 true From 70608f8d10770a976298ca8bb7aea13980d13a63 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 15:29:43 -0800 Subject: [PATCH 128/133] Mess around with dependencies to avoid vulnerabilities. --- core/pom.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 4e516a41..98fc070b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -72,7 +72,13 @@ com.azure azure-security-keyvault-keys - 4.5.2 + 4.3.8 + true + + + com.fasterxml.jackson.core + jackson-databind + 2.13.4.2 true From e5c394d4a983266eb90d4b789db089b38be18980 Mon Sep 17 00:00:00 2001 From: George Khaburzaniya Date: Fri, 18 Nov 2022 15:35:48 -0800 Subject: [PATCH 129/133] Lower dependency values. --- core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 98fc070b..bcf90b8b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,7 +48,7 @@ joda-time joda-time - 2.10.14 + 2.10.6 @@ -72,7 +72,7 @@ com.azure azure-security-keyvault-keys - 4.3.8 + 4.3.4 true From 5549c7c7f87d56a2415b7be5abdc22f7c5e0e6f8 Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 10 Oct 2023 02:49:56 +0200 Subject: [PATCH 130/133] Fix tests --- core/pom.xml | 5 ---- .../saml2/test/authn/AuthnRequestTest.java | 1 - .../saml2/test/authn/AuthnResponseTest.java | 1 - .../saml2/test/logout/LogoutRequestTest.java | 24 ++++++++++--------- .../saml2/test/logout/LogoutResponseTest.java | 11 +++++---- .../test/settings/IdPMetadataParserTest.java | 2 -- .../onelogin/saml2/test/util/UtilsTest.java | 11 +++++---- .../invalids/invalid_issuer.xml | 4 ++-- .../invalids/invalid_issuer.xml.base64 | 2 +- 9 files changed, 29 insertions(+), 32 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9d7ccebd..9ded36ae 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -27,11 +27,6 @@ junit test - - org.mockito - mockito-core - test - diff --git a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java index 20886fb8..5ef9981c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/authn/AuthnRequestTest.java @@ -13,7 +13,6 @@ import java.util.Calendar; import java.util.List; -import org.junit.Assert; import org.junit.Test; import com.onelogin.saml2.authn.AuthnRequest; 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 7cec9efa..17c9087f 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 @@ -5,7 +5,6 @@ import com.onelogin.saml2.exception.SettingsException; import com.onelogin.saml2.exception.ValidationError; import com.onelogin.saml2.http.HttpRequest; -import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.model.SamlResponseStatus; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java index c81f172e..4318c1f9 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutRequestTest.java @@ -858,25 +858,26 @@ public void testIsInValidSign_defaultUrlEncode() throws Exception { settings.setWantMessagesSigned(true); final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; - String samlRequestEncoded = "lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE="; + String samlRequestEncoded = "lVLBitswEP0Vo7tjWbJkSyReFkIhsN1tm6WHvQTZHmdFbUmVZLqfXzlpIS10oZdhGM17b96MtkHNk5MP9myX+AW+LxBi9jZPJsjLyw4t3kirgg7SqBmCjL083n98kGSDpfM22t5O6AbyPkKFAD5qa1B22O/QSWA+EFWPjCtaM6gBugrXHCo6Ut6UgvTV2DSkBoKyr+BDQu5QIkrwEBY4mBCViamEyyrHNCf4ueSScMnIC8r2yY02Kl5QrzG6IIvC6dgt07eNsbl2G+vPhYEf1sBkz9oUA8y2LLQZ4G3jXt1dmALKHm18Mk/+fozgk5YQNMciJ+UzKWV11Wq3q3l5mcq3/9YKenYTrL3FGkihB1fMENWgoloVt8Ut0ZX1Me3xsM+On9bk86ImPep1kv+xdKuBsg/Wzyq+f6u1ood8vLTK6JUJGkxE7WnsSDcQRirOKMc97TtWCgqU1ZyJBvM+RZbSrv/l5mrg6sbJI4T1kId1ye0JhoaQgYg+XT1dnilMSZO4uko1jPSYVF0luqQjrmR/4X8X//jC7U8="; String relayState = "_1037fbc88ec82ce8e770b2bed1119747bb812a07e6"; - String sigAlg = Constants.SHA256; + String sigAlg = Constants.RSA_SHA256; String queryString = "SAMLRequest=" + Util.urlEncoder(samlRequestEncoded); queryString += "&RelayState=" + Util.urlEncoder(relayState); queryString += "&SigAlg=" + Util.urlEncoder(sigAlg); //This signature is based on the query string above - String signature = "cxDTcLRHhXJKGYcjZE2RRz5p7tVg/irNimq48KkJ0n10wiGwAmuzUByxEm4OHbetDrHGtxI5ygjrR0/HcrD8IkYyI5Ie4r5tJYkfdtpUrvOQ7khbBvP9GzEbZIrz7eH1ALdCDchORaRB/cs6v+OZbBj5uPTrN//wOhZl2k9H2xVW/SYy17jDoIKh/wvqtQ9FF+h2UxdUEhxeB/UUXOC6nVLMo+RGaamSviYkUE1Zu1tmalO+F6FivNQ31T/TkqzWz0KEjmnFs3eKbHakPVuUHpDQm7Gf2gBS1TXwVQsL7e2axtvv4RH5djlq1Z2WH2V+PwGOkIvLxf3igGUSR1A8bw=="; + String signature = "27tdJT0kmletQ/fSUhB6Y8L0S6Y7pcZlGFvOCCVcqZZDyxlZBaCfmLlDXhB3/oJrWRn8injiY44h1BnCsughYQjTGBWZi175J9HA/dYhMZ+IFw9V/oUrRTY8/o9kFQSIefhQcJoegY2BvJVDSKeqYg2mCcQnItyceLhS1eiEQy0="; - HttpRequest httpRequest = new HttpRequest(requestURL, queryString) + HttpRequest httpRequest = new HttpRequest(requestURL) .addParameter("SAMLRequest", samlRequestEncoded) .addParameter("RelayState", relayState) .addParameter("SigAlg", sigAlg) .addParameter("Signature", signature); LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); - assertTrue("Signature validation failed", logoutRequest.isValid()); + assertFalse(logoutRequest.isValid()); + assertEquals("Signature validation failed. Logout Request rejected", logoutRequest.getError()); } @Test @@ -886,16 +887,16 @@ public void testIsInValidSign_naiveUrlEncoding() throws Exception { settings.setWantMessagesSigned(true); final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; - String samlRequestEncoded = "lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE="; + String samlRequestEncoded = "lVLBitswEP0Vo7tjWbJkSyReFkIhsN1tm6WHvQTZHmdFbUmVZLqfXzlpIS10oZdhGM17b96MtkHNk5MP9myX+AW+LxBi9jZPJsjLyw4t3kirgg7SqBmCjL083n98kGSDpfM22t5O6AbyPkKFAD5qa1B22O/QSWA+EFWPjCtaM6gBugrXHCo6Ut6UgvTV2DSkBoKyr+BDQu5QIkrwEBY4mBCViamEyyrHNCf4ueSScMnIC8r2yY02Kl5QrzG6IIvC6dgt07eNsbl2G+vPhYEf1sBkz9oUA8y2LLQZ4G3jXt1dmALKHm18Mk/+fozgk5YQNMciJ+UzKWV11Wq3q3l5mcq3/9YKenYTrL3FGkihB1fMENWgoloVt8Ut0ZX1Me3xsM+On9bk86ImPep1kv+xdKuBsg/Wzyq+f6u1ood8vLTK6JUJGkxE7WnsSDcQRirOKMc97TtWCgqU1ZyJBvM+RZbSrv/l5mrg6sbJI4T1kId1ye0JhoaQgYg+XT1dnilMSZO4uko1jPSYVF0luqQjrmR/4X8X//jC7U8="; String relayState = "_1037fbc88ec82ce8e770b2bed1119747bb812a07e6"; - String sigAlg = Constants.SHA256; + String sigAlg = Constants.RSA_SHA256; String queryString = "SAMLRequest=" + NaiveUrlEncoder.encode(samlRequestEncoded); queryString += "&RelayState=" + NaiveUrlEncoder.encode(relayState); queryString += "&SigAlg=" + NaiveUrlEncoder.encode(sigAlg); //This signature is based on the query string above - String signatureNaiveEncoding = "Gj2mUq6RBPAPXI9VjDDlwAxueSEBlOfgpWKLpsQbqIp+2XPFtC/vPAZpuPjHCDNNnAI3WKZa4l8ijwQBTqQwKz88k9gTx6vcLxPl2L4SrWdLOokiGrIVYJ+0sK2hapHHMa7WzGiTgpeTuejHbD4ptneaRXl4nrJAEo0WJ/rNTSWbJpnb+ENtgBnsfkmj+6z1KFY70ruo7W/vme21Jg+4XNfBSGl6LLSjEnZHJG0ET80HKvJEZayv4BQGZ3MShcSMyab/w+rLfDvDRA5RcRxw+NHOXo/kxZ3qhpa6daOwG69+PiiWmusmB2gaSq6jy2L55zFks9a36Pt5l5fYA2dE4g=="; + String signatureNaiveEncoding = "j/qDRTzgQw3cMDkkSkBOShqxi3t9qJxYnrADqwAECnJ3Y+iYgT33C0l/Vy3+ooQkFRyObYJqg9o7iIcMdgV6CXxpa6itVIUAI2VJewsMjzvJ4OdpePeSx7+/umVPKCfMvffsELlqo/UgxsyRZh8NMLej0ojCB7bUfIMKsiU7e0c="; HttpRequest httpRequest = new HttpRequest(requestURL, queryString) .addParameter("SAMLRequest", samlRequestEncoded) @@ -904,7 +905,8 @@ public void testIsInValidSign_naiveUrlEncoding() throws Exception { .addParameter("Signature", signatureNaiveEncoding); LogoutRequest logoutRequest = new LogoutRequest(settings, httpRequest); - assertTrue("Signature validation failed", logoutRequest.isValid()); + assertFalse(logoutRequest.isValid()); + assertEquals("Signature validation failed. Logout Request rejected", logoutRequest.getError()); } /** @@ -921,10 +923,10 @@ public void testIsInValidSign() throws Exception { settings.setWantMessagesSigned(true); final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; - String samlRequestEncoded = "lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE="; + String samlRequestEncoded = "lVLBitswEP0Vo7tjWbJkSyReFkIhsN1tm6WHvQTZHmdFbUmVZLqfXzlpIS10oZdhGM17b96MtkHNk5MP9myX+AW+LxBi9jZPJsjLyw4t3kirgg7SqBmCjL083n98kGSDpfM22t5O6AbyPkKFAD5qa1B22O/QSWA+EFWPjCtaM6gBugrXHCo6Ut6UgvTV2DSkBoKyr+BDQu5QIkrwEBY4mBCViamEyyrHNCf4ueSScMnIC8r2yY02Kl5QrzG6IIvC6dgt07eNsbl2G+vPhYEf1sBkz9oUA8y2LLQZ4G3jXt1dmALKHm18Mk/+fozgk5YQNMciJ+UzKWV11Wq3q3l5mcq3/9YKenYTrL3FGkihB1fMENWgoloVt8Ut0ZX1Me3xsM+On9bk86ImPep1kv+xdKuBsg/Wzyq+f6u1ood8vLTK6JUJGkxE7WnsSDcQRirOKMc97TtWCgqU1ZyJBvM+RZbSrv/l5mrg6sbJI4T1kId1ye0JhoaQgYg+XT1dnilMSZO4uko1jPSYVF0luqQjrmR/4X8X//jC7U8="; String relayState = "_1037fbc88ec82ce8e770b2bed1119747bb812a07e6"; String sigAlg = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; - String signature = "XCwCyI5cs7WhiJlB5ktSlWxSBxv+6q2xT3c8L7dLV6NQG9LHWhN7gf8qNsahSXfCzA0Ey9dp5BQ0EdRvAk2DIzKmJY6e3hvAIEp1zglHNjzkgcQmZCcrkK9Czi2Y1WkjOwR/WgUTUWsGJAVqVvlRZuS3zk3nxMrLH6f7toyvuJc="; + String signature = "j/qDRTzgQw3cMDkkSkBOShqxi3t9qJxYnrADqwAECnJ3Y+iYgT33C0l/Vy3+ooQkFRyObYJqg9o7iIcMdgV6CXxpa6itVIUAI2VJewsMjzvJ4OdpePeSx7+/umVPKCfMvffsELlqo/UgxsyRZh8NMLej0ojCB7bUfIMKsiU7e0c="; HttpRequest httpRequest = new HttpRequest(requestURL, (String)null) .addParameter("SAMLRequest", samlRequestEncoded) diff --git a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java index dbc68c51..47a2a1c8 100644 --- a/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/logout/LogoutResponseTest.java @@ -23,6 +23,7 @@ import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.exception.XMLEntityException; import com.onelogin.saml2.http.HttpRequest; +import com.onelogin.saml2.logout.LogoutRequest; import com.onelogin.saml2.logout.LogoutResponse; import com.onelogin.saml2.logout.LogoutResponseParams; import com.onelogin.saml2.model.SamlResponseStatus; @@ -697,7 +698,7 @@ public void testIsInValidSign_defaultUrlEncode() throws Exception { final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; String samlResponseEncoded = "fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A"; String relayState = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php"; - String sigAlg = Constants.SHA256; + String sigAlg = Constants.RSA_SHA256; String queryString = "SAMLResponse=" + Util.urlEncoder(samlResponseEncoded); queryString += "&RelayState=" + Util.urlEncoder(relayState); @@ -713,7 +714,8 @@ public void testIsInValidSign_defaultUrlEncode() throws Exception { .addParameter("Signature", signature); LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); - assertTrue("Signature validation failed", logoutResponse.isValid()); + assertFalse(logoutResponse.isValid()); + assertEquals("Signature validation failed. Logout Response rejected", logoutResponse.getError()); } @Test @@ -725,7 +727,7 @@ public void testIsInValidSign_naiveUrlEncoding() throws Exception { final String requestURL = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls"; String samlResponseEncoded = "fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A"; String relayState = "https://pitbulk.no-ip.org/newonelogin/demo1/index.php"; - String sigAlg = Constants.SHA256; + String sigAlg = Constants.RSA_SHA256; String queryString = "SAMLResponse=" + NaiveUrlEncoder.encode(samlResponseEncoded); queryString += "&RelayState=" + NaiveUrlEncoder.encode(relayState); @@ -741,7 +743,8 @@ public void testIsInValidSign_naiveUrlEncoding() throws Exception { .addParameter("Signature", signature); LogoutResponse logoutResponse = new LogoutResponse(settings, httpRequest); - assertTrue("Signature validation failed", logoutResponse.isValid()); + assertFalse(logoutResponse.isValid()); + assertEquals("Signature validation failed. Logout Response rejected", logoutResponse.getError()); } /** diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index c3c25e44..265dfcf8 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -5,14 +5,12 @@ import static org.junit.Assert.assertNull; import java.net.URL; -import java.util.List; import java.util.Map; import org.junit.Test; import org.w3c.dom.Document; import org.xml.sax.InputSource; -import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.settings.IdPMetadataParser; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; 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 13012543..da0974df 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 @@ -14,8 +14,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.io.FileNotFoundException; import java.io.IOException; @@ -37,6 +35,8 @@ import java.util.Date; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; @@ -1519,9 +1519,10 @@ public void testAddSignDocEmpty() throws URISyntaxException, IOException, Genera PrivateKey key = Util.loadPrivateKey(keyString); String signAlgorithmSha1 = Constants.RSA_SHA1; - Document emptyDoc = mock(Document.class); - when(emptyDoc.getDocumentElement()).thenReturn(null); - + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = dbf.newDocumentBuilder(); + Document emptyDoc = builder.newDocument(); + assertNull(emptyDoc.getDocumentElement()); String docSigned = Util.addSign(emptyDoc, key, cert, signAlgorithmSha1); } diff --git a/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml b/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml index e1edabde..89e00e55 100644 --- a/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml +++ b/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml @@ -5,10 +5,10 @@ Version="2.0" IssueInstant="2013-12-10T04:39:31Z" Destination="http://stuff.com/endpoints/endpoints/sls.php" - NotOnOrAfter="2023-05-10T04:39:31Z" + NotOnOrAfter="2993-05-10T04:39:31Z" > https://example.hello.com/access/saml ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c - + \ No newline at end of file diff --git a/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml.base64 b/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml.base64 index 56de49b0..b60c4002 100644 --- a/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml.base64 +++ b/core/src/test/resources/data/logout_requests/invalids/invalid_issuer.xml.base64 @@ -1 +1 @@ -jZPfb4IwEMffTfwfDO9CW8BJoxgTt8XEyTaXPexl6coxSaBlXFn881dwP3yQxT411+v3+7n2brY4lMXoE2rMtZo71CXOIh4OZijKouIb/a4b8wgfDaAZ2UyFvDuZO02tuBaYI1eiBORG8t3ybsOZS3hVa6OlLpzhYHRu/Qn9ryMQoTYWrE9ovZo7yfZ6k9yut6+MhtNAyjQTUvgTQYNQQBSRIGCpTKPJWwYTQkMKfWLPP49grXsNERtYKzRCGZtIqD+mbEzJEwm4H3GfvvTdXNkXzJUwncPemIp7Hpomy1ypSw9UWulcGTzZYYFuta/6BLfaJCqpl5mBukVh/piEF6HEx3j3x7yrqI5bIrRIcBBlVYC7h6LQHZqQEtDS2OSZd3rlVGVr/269Gu3u281DI4o8y1usi2TPcd7ouhSmvz2oS7tIno6zLpU3CiuQrW96TjD+7RMKtiUkZRHQbMqYnJJoQqa2YSjxZQjA5JUUQOV3tcfS2pnwzgxF/AU= \ No newline at end of file +jZPLboMwEEX3kfIPiH3A5pEGKyGKlLZCSkPbVF10U7lmaJDApoyp8vk1pI8sQhWvrPH43jP2zHx5qErrExoslFzY1CH2Mh6P5sirsmYb9a5a/QgfLaC2TKZE1p8s7LaRTHEskEleATIt2G51t2GeQ1jdKK2EKu3xyDq3/oT+1+GI0GgDNiSUrBd2ur3epLfJ9tWj4SwQIsu54P6U0yDkEEUkCLxMZNH0LYcpoSGFIbHnn0cw1oOGiC0kEjWX2iQS6k+oN6HkiQTMj5hPX4Zurs0LFpLr3mGvdc1cF3Wb545QlQsyq1UhNZ7ssESn3tdDglulU5k2q1xDY1CiyJ+Q8CKU+Bjv/5j1FTVxR4QGCQ68qktw9lCWqkfjQgAaGpM8d0+vnKpszd8la2t3320eWl4WedFhXSR7jvNGNRXXw+1BHdpHimyS96mslViD6Hyzc4Lxb59QMC0hqBcBzWeeJ2YkmpKZaRhKfBECeOJKcKDiu9pjad1MuGeGIv4C \ No newline at end of file From 7b2eac94f553b49071829ed4bc5869d6be1ce20c Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 10 Oct 2023 03:33:53 +0200 Subject: [PATCH 131/133] Update dependencies --- core/pom.xml | 12 +++--------- pom.xml | 4 ++-- toolkit/pom.xml | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 9ded36ae..271d6f7f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -48,7 +48,7 @@ org.apache.santuario xmlsec - 2.3.0 + 3.0.2 commons-codec @@ -60,19 +60,13 @@ com.azure azure-security-keyvault-keys - 4.3.4 - true - - - com.fasterxml.jackson.core - jackson-databind - 2.13.4.2 + 4.7.0 true com.azure azure-identity - 1.3.7 + 1.10.1 true diff --git a/pom.xml b/pom.xml index d6ebe7a8..9e51d393 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ org.mockito mockito-core - 1.10.19 + 3.6.28 test @@ -96,7 +96,7 @@ org.owasp dependency-check-maven - 6.3.1 + 8.4.0 7 diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 3cb2ab03..048688bf 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -74,7 +74,7 @@ org.apache.santuario xmlsec - 2.2.3 + 3.0.2 commons-codec From bbca73a0fa05cb874486376ad3699f53a77905df Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Tue, 10 Oct 2023 04:04:15 +0200 Subject: [PATCH 132/133] Update more dependencies --- core/pom.xml | 4 ++-- pom.xml | 8 ++++---- toolkit/pom.xml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 271d6f7f..8687059f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -53,7 +53,7 @@ commons-codec commons-codec - 1.15 + 1.16.0 @@ -76,7 +76,7 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.10 jacoco.agent.argLine diff --git a/pom.xml b/pom.xml index 9e51d393..e2e39801 100644 --- a/pom.xml +++ b/pom.xml @@ -12,10 +12,10 @@ UTF-8 UTF-8 - 1.7.35 + 1.7.36 4.13.2 - 1.2.10 - 3.12.0 + 1.2.12 + 3.13.0 @@ -36,7 +36,7 @@ org.mockito mockito-core - 3.6.28 + 3.12.4 test diff --git a/toolkit/pom.xml b/toolkit/pom.xml index 048688bf..69d723b1 100644 --- a/toolkit/pom.xml +++ b/toolkit/pom.xml @@ -79,7 +79,7 @@ commons-codec commons-codec - 1.15 + 1.16.0 @@ -88,7 +88,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.10 jacoco.agent.argLine From 66853fc78245ad94deedb958bc3453673806081f Mon Sep 17 00:00:00 2001 From: Lapo Luchini Date: Wed, 28 Feb 2024 11:04:12 +0100 Subject: [PATCH 133/133] Normalize EOLs. --- .gitattributes | 4 + .../saml2/settings/IdPMetadataParser.java | 612 +++++++++--------- .../test/settings/IdPMetadataParserTest.java | 606 ++++++++--------- 3 files changed, 613 insertions(+), 609 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ed67b196 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.java text eol=lf +*.jsp text eol=lf +*.xml text eol=lf +*.properties text eol=lf diff --git a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java index 42ee439c..e131d5a7 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java +++ b/core/src/main/java/com/onelogin/saml2/settings/IdPMetadataParser.java @@ -1,306 +1,306 @@ -package com.onelogin.saml2.settings; - -import java.io.InputStream; -import java.net.URL; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.xml.xpath.XPathException; -import javax.xml.xpath.XPathExpressionException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; - -import com.onelogin.saml2.exception.Error; -import com.onelogin.saml2.util.Constants; -import com.onelogin.saml2.util.Util; - -/** - * IdPMetadataParser class of Java Toolkit. - * - * A class that implements the settings parser from IdP Metadata - * - * This class does not validate in any way the URL that is introduced, - * make sure to validate it properly before use it in a get_metadata method. - */ -public class IdPMetadataParser { - - /** - * Private property to construct a logger for this class. - */ - private static final Logger LOGGER = LoggerFactory.getLogger(IdPMetadataParser.class); - - /** - * Get IdP Metadata Info from XML Document - * - * @param xmlDocument - * XML document hat contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * @param desiredNameIdFormat - * If available on IdP metadata, use that nameIdFormat - * @param desiredSSOBinding - * Parse specific binding SSO endpoint. - * @param desiredSLOBinding - * Parse specific binding SLO endpoint. - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws XPathExpressionException - */ - public static Map parseXML(Document xmlDocument, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws XPathException { - Map metadataInfo = new LinkedHashMap<>(); - - try { - String customIdPStr = ""; - if (entityId != null && !entityId.isEmpty()) { - customIdPStr = "[@entityID=\"" + entityId + "\"]"; - } - - String idpDescryptorXPath = "//md:EntityDescriptor" + customIdPStr + "/md:IDPSSODescriptor"; - - NodeList idpDescriptorNodes = Util.query(xmlDocument, idpDescryptorXPath); - - if (idpDescriptorNodes.getLength() > 0) { - - Node idpDescriptorNode = idpDescriptorNodes.item(0); - if (entityId == null || entityId.isEmpty()) { - Node entityIDNode = idpDescriptorNode.getParentNode().getAttributes().getNamedItem("entityID"); - if (entityIDNode != null) { - entityId = entityIDNode.getNodeValue(); - } - } - - if (entityId != null && !entityId.isEmpty()) { - metadataInfo.put(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY, entityId); - } - - NodeList ssoNodes = Util.query(xmlDocument, "./md:SingleSignOnService[@Binding=\"" + desiredSSOBinding + "\"]", idpDescriptorNode); - if (ssoNodes.getLength() < 1) { - ssoNodes = Util.query(xmlDocument, "./md:SingleSignOnService", idpDescriptorNode); - } - if (ssoNodes.getLength() > 0) { - metadataInfo.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, ssoNodes.item(0).getAttributes().getNamedItem("Location").getNodeValue()); - metadataInfo.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY, ssoNodes.item(0).getAttributes().getNamedItem("Binding").getNodeValue()); - } - - NodeList sloNodes = Util.query(xmlDocument, "./md:SingleLogoutService[@Binding=\"" + desiredSLOBinding + "\"]", idpDescriptorNode); - if (sloNodes.getLength() < 1) { - sloNodes = Util.query(xmlDocument, "./md:SingleLogoutService", idpDescriptorNode); - } - if (sloNodes.getLength() > 0) { - metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Location").getNodeValue()); - metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Binding").getNodeValue()); - Node responseLocationNode = sloNodes.item(0).getAttributes().getNamedItem("ResponseLocation"); - if (responseLocationNode != null) { - metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY, responseLocationNode.getNodeValue()); - } - } - - NodeList keyDescriptorCertSigningNodes = Util.query(xmlDocument, "./md:KeyDescriptor[not(contains(@use, \"encryption\"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate", - idpDescriptorNode); - - NodeList keyDescriptorCertEncryptionNodes = Util.query(xmlDocument, "./md:KeyDescriptor[not(contains(@use, \"signing\"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate", - idpDescriptorNode); - - if (keyDescriptorCertSigningNodes.getLength() > 0 || keyDescriptorCertEncryptionNodes.getLength() > 0) { - - boolean hasEncryptionCert = keyDescriptorCertEncryptionNodes.getLength() > 0; - String encryptionCert = null; - - if (hasEncryptionCert) { - encryptionCert = keyDescriptorCertEncryptionNodes.item(0).getTextContent(); - metadataInfo.put(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY, encryptionCert); - } - - if (keyDescriptorCertSigningNodes.getLength() > 0) { - int index = 0; - for (int i = 0; i < keyDescriptorCertSigningNodes.getLength(); i++) { - String signingCert = keyDescriptorCertSigningNodes.item(i).getTextContent(); - if (i == 0 && !hasEncryptionCert) { - metadataInfo.put(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY, signingCert); - } else if (!hasEncryptionCert || !encryptionCert.equals(signingCert)) { - metadataInfo.put(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + (index++), signingCert); - } - } - } - } - - NodeList nameIdFormatNodes = Util.query(xmlDocument, "./md:NameIDFormat", idpDescriptorNode); - for (int i = 0; i < nameIdFormatNodes.getLength(); i++) { - String nameIdFormat = nameIdFormatNodes.item(i).getTextContent(); - if (nameIdFormat != null && (desiredNameIdFormat == null || desiredNameIdFormat.equals(nameIdFormat))) { - metadataInfo.put(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY, nameIdFormat); - break; - } - } - } - } catch (XPathException e) { - String errorMsg = "Error parsing metadata. " + e.getMessage(); - LOGGER.error(errorMsg, e); - throw e; - } - - return metadataInfo; - } - - /** - * Get IdP Metadata Info from XML Document - * - * @param xmlDocument - * XML document that contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws XPathException - */ - public static Map parseXML(Document xmlDocument, String entityId) throws XPathException { - return parseXML(xmlDocument, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); - } - - /** - * Get IdP Metadata Info from XML Document - * - * @param xmlDocument - * XML document that contains IdP metadata - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws XPathException - */ - public static Map parseXML(Document xmlDocument) throws XPathException { - return parseXML(xmlDocument, null); - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlFileName - * Filename of the XML filename that contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * @param desiredNameIdFormat - * If available on IdP metadata, use that nameIdFormat - * @param desiredSSOBinding - * Parse specific binding SSO endpoint. - * @param desiredSLOBinding - * Parse specific binding SLO endpoint. - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseFileXML(String xmlFileName, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws Exception { - ClassLoader classLoader = IdPMetadataParser.class.getClassLoader(); - try (InputStream inputStream = classLoader.getResourceAsStream(xmlFileName)) { - if (inputStream != null) { - Document xmlDocument = Util.parseXML(new InputSource(inputStream)); - return parseXML(xmlDocument, entityId, desiredNameIdFormat, desiredSSOBinding, desiredSLOBinding); - } else { - throw new Exception("XML file '" + xmlFileName + "' not found in the classpath"); - } - } catch (Exception e) { - String errorMsg = "XML file'" + xmlFileName + "' cannot be loaded." + e.getMessage(); - LOGGER.error(errorMsg, e); - throw new Error(errorMsg, Error.SETTINGS_FILE_NOT_FOUND); - } - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlFileName - * Filename of the XML filename that contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseFileXML(String xmlFileName, String entityId) throws Exception { - return parseFileXML(xmlFileName, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlFileName - * Filename of the XML filename that contains IdP metadata - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseFileXML(String xmlFileName) throws Exception { - return parseFileXML(xmlFileName, null); - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlURL - * URL to the XML document that contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * @param desiredNameIdFormat - * If available on IdP metadata, use that nameIdFormat - * @param desiredSSOBinding - * Parse specific binding SSO endpoint. - * @param desiredSLOBinding - * Parse specific binding SLO endpoint. - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseRemoteXML(URL xmlURL, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws Exception { - Document xmlDocument = Util.parseXML(new InputSource(xmlURL.openStream())); - return parseXML(xmlDocument, entityId, desiredNameIdFormat, desiredSSOBinding, desiredSLOBinding); - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlURL - * URL to the XML document that contains IdP metadata - * @param entityId - * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseRemoteXML(URL xmlURL, String entityId) throws Exception { - return parseRemoteXML(xmlURL, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); - } - - /** - * Get IdP Metadata Info from XML file - * - * @param xmlURL - * URL to the XML document that contains IdP metadata - * - * @return Mapped values with metadata info in Saml2Settings format - * @throws Exception - */ - public static Map parseRemoteXML(URL xmlURL) throws Exception { - return parseRemoteXML(xmlURL, null); - } - - /** - * Inject metadata info into Saml2Settings - * - * @param settings - * the Saml2Settings object - * @param metadataInfo - * mapped values with metadata info in Saml2Settings format - * - * @return the Saml2Settings object with metadata info settings loaded - */ - public static Saml2Settings injectIntoSettings(Saml2Settings settings, Map metadataInfo) { - - SettingsBuilder settingsBuilder = new SettingsBuilder().fromValues(metadataInfo); - settingsBuilder.build(settings); - return settings; - } - -} +package com.onelogin.saml2.settings; + +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.xml.xpath.XPathException; +import javax.xml.xpath.XPathExpressionException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import com.onelogin.saml2.exception.Error; +import com.onelogin.saml2.util.Constants; +import com.onelogin.saml2.util.Util; + +/** + * IdPMetadataParser class of Java Toolkit. + * + * A class that implements the settings parser from IdP Metadata + * + * This class does not validate in any way the URL that is introduced, + * make sure to validate it properly before use it in a get_metadata method. + */ +public class IdPMetadataParser { + + /** + * Private property to construct a logger for this class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(IdPMetadataParser.class); + + /** + * Get IdP Metadata Info from XML Document + * + * @param xmlDocument + * XML document hat contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * @param desiredNameIdFormat + * If available on IdP metadata, use that nameIdFormat + * @param desiredSSOBinding + * Parse specific binding SSO endpoint. + * @param desiredSLOBinding + * Parse specific binding SLO endpoint. + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws XPathExpressionException + */ + public static Map parseXML(Document xmlDocument, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws XPathException { + Map metadataInfo = new LinkedHashMap<>(); + + try { + String customIdPStr = ""; + if (entityId != null && !entityId.isEmpty()) { + customIdPStr = "[@entityID=\"" + entityId + "\"]"; + } + + String idpDescryptorXPath = "//md:EntityDescriptor" + customIdPStr + "/md:IDPSSODescriptor"; + + NodeList idpDescriptorNodes = Util.query(xmlDocument, idpDescryptorXPath); + + if (idpDescriptorNodes.getLength() > 0) { + + Node idpDescriptorNode = idpDescriptorNodes.item(0); + if (entityId == null || entityId.isEmpty()) { + Node entityIDNode = idpDescriptorNode.getParentNode().getAttributes().getNamedItem("entityID"); + if (entityIDNode != null) { + entityId = entityIDNode.getNodeValue(); + } + } + + if (entityId != null && !entityId.isEmpty()) { + metadataInfo.put(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY, entityId); + } + + NodeList ssoNodes = Util.query(xmlDocument, "./md:SingleSignOnService[@Binding=\"" + desiredSSOBinding + "\"]", idpDescriptorNode); + if (ssoNodes.getLength() < 1) { + ssoNodes = Util.query(xmlDocument, "./md:SingleSignOnService", idpDescriptorNode); + } + if (ssoNodes.getLength() > 0) { + metadataInfo.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, ssoNodes.item(0).getAttributes().getNamedItem("Location").getNodeValue()); + metadataInfo.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY, ssoNodes.item(0).getAttributes().getNamedItem("Binding").getNodeValue()); + } + + NodeList sloNodes = Util.query(xmlDocument, "./md:SingleLogoutService[@Binding=\"" + desiredSLOBinding + "\"]", idpDescriptorNode); + if (sloNodes.getLength() < 1) { + sloNodes = Util.query(xmlDocument, "./md:SingleLogoutService", idpDescriptorNode); + } + if (sloNodes.getLength() > 0) { + metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Location").getNodeValue()); + metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, sloNodes.item(0).getAttributes().getNamedItem("Binding").getNodeValue()); + Node responseLocationNode = sloNodes.item(0).getAttributes().getNamedItem("ResponseLocation"); + if (responseLocationNode != null) { + metadataInfo.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY, responseLocationNode.getNodeValue()); + } + } + + NodeList keyDescriptorCertSigningNodes = Util.query(xmlDocument, "./md:KeyDescriptor[not(contains(@use, \"encryption\"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate", + idpDescriptorNode); + + NodeList keyDescriptorCertEncryptionNodes = Util.query(xmlDocument, "./md:KeyDescriptor[not(contains(@use, \"signing\"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate", + idpDescriptorNode); + + if (keyDescriptorCertSigningNodes.getLength() > 0 || keyDescriptorCertEncryptionNodes.getLength() > 0) { + + boolean hasEncryptionCert = keyDescriptorCertEncryptionNodes.getLength() > 0; + String encryptionCert = null; + + if (hasEncryptionCert) { + encryptionCert = keyDescriptorCertEncryptionNodes.item(0).getTextContent(); + metadataInfo.put(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY, encryptionCert); + } + + if (keyDescriptorCertSigningNodes.getLength() > 0) { + int index = 0; + for (int i = 0; i < keyDescriptorCertSigningNodes.getLength(); i++) { + String signingCert = keyDescriptorCertSigningNodes.item(i).getTextContent(); + if (i == 0 && !hasEncryptionCert) { + metadataInfo.put(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY, signingCert); + } else if (!hasEncryptionCert || !encryptionCert.equals(signingCert)) { + metadataInfo.put(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + (index++), signingCert); + } + } + } + } + + NodeList nameIdFormatNodes = Util.query(xmlDocument, "./md:NameIDFormat", idpDescriptorNode); + for (int i = 0; i < nameIdFormatNodes.getLength(); i++) { + String nameIdFormat = nameIdFormatNodes.item(i).getTextContent(); + if (nameIdFormat != null && (desiredNameIdFormat == null || desiredNameIdFormat.equals(nameIdFormat))) { + metadataInfo.put(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY, nameIdFormat); + break; + } + } + } + } catch (XPathException e) { + String errorMsg = "Error parsing metadata. " + e.getMessage(); + LOGGER.error(errorMsg, e); + throw e; + } + + return metadataInfo; + } + + /** + * Get IdP Metadata Info from XML Document + * + * @param xmlDocument + * XML document that contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws XPathException + */ + public static Map parseXML(Document xmlDocument, String entityId) throws XPathException { + return parseXML(xmlDocument, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); + } + + /** + * Get IdP Metadata Info from XML Document + * + * @param xmlDocument + * XML document that contains IdP metadata + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws XPathException + */ + public static Map parseXML(Document xmlDocument) throws XPathException { + return parseXML(xmlDocument, null); + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlFileName + * Filename of the XML filename that contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * @param desiredNameIdFormat + * If available on IdP metadata, use that nameIdFormat + * @param desiredSSOBinding + * Parse specific binding SSO endpoint. + * @param desiredSLOBinding + * Parse specific binding SLO endpoint. + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseFileXML(String xmlFileName, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws Exception { + ClassLoader classLoader = IdPMetadataParser.class.getClassLoader(); + try (InputStream inputStream = classLoader.getResourceAsStream(xmlFileName)) { + if (inputStream != null) { + Document xmlDocument = Util.parseXML(new InputSource(inputStream)); + return parseXML(xmlDocument, entityId, desiredNameIdFormat, desiredSSOBinding, desiredSLOBinding); + } else { + throw new Exception("XML file '" + xmlFileName + "' not found in the classpath"); + } + } catch (Exception e) { + String errorMsg = "XML file'" + xmlFileName + "' cannot be loaded." + e.getMessage(); + LOGGER.error(errorMsg, e); + throw new Error(errorMsg, Error.SETTINGS_FILE_NOT_FOUND); + } + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlFileName + * Filename of the XML filename that contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseFileXML(String xmlFileName, String entityId) throws Exception { + return parseFileXML(xmlFileName, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlFileName + * Filename of the XML filename that contains IdP metadata + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseFileXML(String xmlFileName) throws Exception { + return parseFileXML(xmlFileName, null); + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlURL + * URL to the XML document that contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * @param desiredNameIdFormat + * If available on IdP metadata, use that nameIdFormat + * @param desiredSSOBinding + * Parse specific binding SSO endpoint. + * @param desiredSLOBinding + * Parse specific binding SLO endpoint. + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseRemoteXML(URL xmlURL, String entityId, String desiredNameIdFormat, String desiredSSOBinding, String desiredSLOBinding) throws Exception { + Document xmlDocument = Util.parseXML(new InputSource(xmlURL.openStream())); + return parseXML(xmlDocument, entityId, desiredNameIdFormat, desiredSSOBinding, desiredSLOBinding); + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlURL + * URL to the XML document that contains IdP metadata + * @param entityId + * Entity Id of the desired IdP, if no entity Id is provided and the XML metadata contains more than one IDPSSODescriptor, the first is returned + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseRemoteXML(URL xmlURL, String entityId) throws Exception { + return parseRemoteXML(xmlURL, entityId, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); + } + + /** + * Get IdP Metadata Info from XML file + * + * @param xmlURL + * URL to the XML document that contains IdP metadata + * + * @return Mapped values with metadata info in Saml2Settings format + * @throws Exception + */ + public static Map parseRemoteXML(URL xmlURL) throws Exception { + return parseRemoteXML(xmlURL, null); + } + + /** + * Inject metadata info into Saml2Settings + * + * @param settings + * the Saml2Settings object + * @param metadataInfo + * mapped values with metadata info in Saml2Settings format + * + * @return the Saml2Settings object with metadata info settings loaded + */ + public static Saml2Settings injectIntoSettings(Saml2Settings settings, Map metadataInfo) { + + SettingsBuilder settingsBuilder = new SettingsBuilder().fromValues(metadataInfo); + settingsBuilder.build(settings); + return settings; + } + +} diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java index 265dfcf8..118eabba 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/IdPMetadataParserTest.java @@ -1,304 +1,304 @@ -package com.onelogin.saml2.test.settings; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertNull; - -import java.net.URL; -import java.util.Map; - -import org.junit.Test; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; - -import com.onelogin.saml2.settings.IdPMetadataParser; -import com.onelogin.saml2.settings.Saml2Settings; -import com.onelogin.saml2.settings.SettingsBuilder; -import com.onelogin.saml2.util.Constants; -import com.onelogin.saml2.util.Util; - -public class IdPMetadataParserTest { - - @Test - public void testParseFileXML() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/onelogin_metadata.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - boolean throwedInvalidXPath = false; - try { - IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml", null, null, "\"", "\""); - } catch (Exception e) { - throwedInvalidXPath = true; - } - assertTrue(throwedInvalidXPath); - - boolean throwedFileNotFound = false; - try { - IdPMetadataParser.parseFileXML("nonexistent.file", null, null, "\"", "\""); - } catch (Exception e) { - throwedFileNotFound = true; - } - assertTrue(throwedFileNotFound); - - } - - @Test - public void testParseXML() throws Exception { - Document xmlDocument = Util.parseXML(new InputSource(getClass().getClassLoader().getResourceAsStream("data/metadata/onelogin_metadata.xml"))); - - Map idpInfo = IdPMetadataParser.parseXML(xmlDocument); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseFileXmlMultix509cert() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + ".0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseFileXmlDesiredBindings() throws Exception { - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml"); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", "https://idp.testshib.org/idp/shibboleth"); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_ARTIFACT, Constants.BINDING_HTTP_ARTIFACT); - assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.testshib.org/idp/profile/Shibboleth/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:mace:shibboleth:1.0:profiles:AuthnRequest", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); - assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - } - - @Test - public void testParseRemoteXML() throws Exception { - - Map idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/645460")); - assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/383123")); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/slo/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIDGTCCAoKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsalDL15zSKeEGy9c0Hao7+G02x6k/MlZuCwEvkPKUcl9QF/q0584lta735hmiZSuWOFQDNQ4VT53VevjAhOtzLJOsa8wcZ+SA1s3j4bNcpUIAHltb4Az6NC7U2/LatfnwscOazEJnVsfL4aaBdpIHBFQ6Ed0StD0AfB6Ci0hURwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHm3fLi+Q1zMc3guMyHy5AHdQvdgMIGRBgNVHSMEgYkwgYaAFHm3fLi+Q1zMc3guMyHy5AHdQvdgoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEANZvzlB1Aq84AdOvsn2XKxBB/PmNZLqnM1VWRPaNcvjafx7eHd5qayXFNQz+bOLujENmgAm5padbydG89SeefpOGcY2TMsVt0RUzxTnN3Zq5G6Ja2fAKOEX01ejdoPPMmStqqSw8k1wPUU8uLYJG5wmjf0rCb8RVaeAwMc+wcEIA=")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiCerts() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_certs.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiSigningCerts() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_signing_certs.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); - assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); - assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseMultiSameSigningAndEncryptCert() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_same_sign_and_encrypt_cert.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - - Map idpInfo2 = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_different_sign_and_encrypt_cert.xml"); - assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo2.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals(Util.loadCert((String) idpInfo2.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( - "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); - assertEquals(Util.loadCert((String)idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( - "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); - assertNull(idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); - assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo2.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); - } - - @Test - public void testParseSeparateSingleLogoutServiceResponseLocation() throws Exception { - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata_slo_responselocation.xml"); - assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); - assertEquals("https://idp.examle.com/saml/sloresp", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY)); - assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); - } - - @Test - public void testInjectIntoSettings() throws Exception { - Saml2Settings setting = new SettingsBuilder().fromFile("config/config.all.properties").build(); - - assertEquals("http://idp.example.com/", setting.getIdpEntityId()); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting.getIdpx509cert()); - assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); - assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); - assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); - assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); - assertEquals(4, setting.getContacts().size()); - assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); - assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); - assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); - assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); - assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); - assertEquals("SP Java", setting.getOrganization().getOrgName()); - assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); - - Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/FederationMetadata.xml"); - setting = IdPMetadataParser.injectIntoSettings(setting, idpInfo); - assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting.getIdpEntityId()); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(setting.getIdpx509cert(), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); - assertEquals(setting.getIdpx509certMulti().get(0), Util.loadCert( - "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); - assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); - assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); - assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); - assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); - assertEquals(4, setting.getContacts().size()); - assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); - assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); - assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); - assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); - assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); - assertEquals("SP Java", setting.getOrganization().getOrgName()); - assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); - - Saml2Settings setting2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); - assertEquals("http://idp.example.com/", setting2.getIdpEntityId()); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting2.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting2.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting2.getIdpx509cert()); - assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); - assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); - assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); - assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); - assertEquals(0, setting2.getContacts().size()); - assertNull(setting2.getOrganization()); - assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); - - setting2 = IdPMetadataParser.injectIntoSettings(setting2, idpInfo); - assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting2.getIdpEntityId()); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleSignOnServiceUrl().toString()); - assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleLogoutServiceUrl().toString()); - assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); - assertEquals(setting2.getIdpx509cert(), Util.loadCert( - "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); - assertEquals(setting2.getIdpx509certMulti().get(0), Util.loadCert( - "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); - assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); - assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); - assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); - assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); - assertEquals(0, setting2.getContacts().size()); - assertNull(setting2.getOrganization()); - assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); - } - +package com.onelogin.saml2.test.settings; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNull; + +import java.net.URL; +import java.util.Map; + +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import com.onelogin.saml2.settings.IdPMetadataParser; +import com.onelogin.saml2.settings.Saml2Settings; +import com.onelogin.saml2.settings.SettingsBuilder; +import com.onelogin.saml2.util.Constants; +import com.onelogin.saml2.util.Util; + +public class IdPMetadataParserTest { + + @Test + public void testParseFileXML() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/onelogin_metadata.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + boolean throwedInvalidXPath = false; + try { + IdPMetadataParser.parseFileXML("data/metadata/idp_metadata.xml", null, null, "\"", "\""); + } catch (Exception e) { + throwedInvalidXPath = true; + } + assertTrue(throwedInvalidXPath); + + boolean throwedFileNotFound = false; + try { + IdPMetadataParser.parseFileXML("nonexistent.file", null, null, "\"", "\""); + } catch (Exception e) { + throwedFileNotFound = true; + } + assertTrue(throwedFileNotFound); + + } + + @Test + public void testParseXML() throws Exception { + Document xmlDocument = Util.parseXML(new InputSource(getClass().getClassLoader().getResourceAsStream("data/metadata/onelogin_metadata.xml"))); + + Map idpInfo = IdPMetadataParser.parseXML(xmlDocument); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://example.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseFileXmlMultix509cert() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + ".0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseFileXmlDesiredBindings() throws Exception { + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml"); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", "https://idp.testshib.org/idp/shibboleth"); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_REDIRECT, Constants.BINDING_HTTP_REDIRECT); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/testshib-providers.xml", null, null, Constants.BINDING_HTTP_ARTIFACT, Constants.BINDING_HTTP_ARTIFACT); + assertEquals("https://idp.testshib.org/idp/shibboleth", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.testshib.org/idp/profile/Shibboleth/SSO", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:mace:shibboleth:1.0:profiles:AuthnRequest", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYDVQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQIEwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRlc3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7CyVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aTNPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWHgWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0GA1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ869nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBlbm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNoaWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRLI4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4/SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAjGeka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==")); + assertEquals("urn:mace:shibboleth:1.0:nameIdentifier", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + } + + @Test + public void testParseRemoteXML() throws Exception { + + Map idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/645460")); + assertEquals("https://app.onelogin.com/saml/metadata/645460", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/sso/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/slo/645460", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + idpInfo = IdPMetadataParser.parseRemoteXML(new URL("https://app.onelogin.com/saml/metadata/383123")); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://try-demo.onelogin.com/trust/saml2/http-redirect/slo/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIDGTCCAoKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsalDL15zSKeEGy9c0Hao7+G02x6k/MlZuCwEvkPKUcl9QF/q0584lta735hmiZSuWOFQDNQ4VT53VevjAhOtzLJOsa8wcZ+SA1s3j4bNcpUIAHltb4Az6NC7U2/LatfnwscOazEJnVsfL4aaBdpIHBFQ6Ed0StD0AfB6Ci0hURwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFHm3fLi+Q1zMc3guMyHy5AHdQvdgMIGRBgNVHSMEgYkwgYaAFHm3fLi+Q1zMc3guMyHy5AHdQvdgoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEANZvzlB1Aq84AdOvsn2XKxBB/PmNZLqnM1VWRPaNcvjafx7eHd5qayXFNQz+bOLujENmgAm5padbydG89SeefpOGcY2TMsVt0RUzxTnN3Zq5G6Ja2fAKOEX01ejdoPPMmStqqSw8k1wPUU8uLYJG5wmjf0rCb8RVaeAwMc+wcEIA=")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiCerts() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_certs.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiSigningCerts() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_multi_signing_certs.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==")); + assertEquals(Util.loadCert((String)idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "1")), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xODAxMTcxNTMzNDNaFw0yMTEwMTMxNTMzNDNaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxejk/DNtB9TlB7PNek/Pds6txAhSbTSIEX6jjKgE170PXCncpkogIO9ae/r3psBll2nU+FbKpnml+Jv81I8nMazQceDg9R4CRnTUV5mwgZShW1DzpEuG3/8TzYcpA41HZQ7Wl7dT19h55speZ8egGptQEcOazMfWmLEI1QhHaowIDAQABo1AwTjAdBgNVHQ4EFgQUmTK9rvir0zDUxKg8iTSh3fMCirowHwYDVR0jBBgwFoAUmTK9rvir0zDUxKg8iTSh3fMCirowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQBhFvvRdguCYT34NJl884UhcmyEBarSBEEajkn73YAvyqhh+yo4LhWIvam/yFLsNdaDzwo9R8wzAaj4XGMPqM4WwSA69RTIv+n5gSgsrgFSja7HhP7Epw8SxpDQiW0ijh/TUTBvWOuqEEhQQvYRwshyJW7n82+wtArH8pnpFUOFuA==")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); + assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseMultiSameSigningAndEncryptCert() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_same_sign_and_encrypt_cert.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertNull(idpInfo.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + + Map idpInfo2 = IdPMetadataParser.parseFileXML("data/metadata/idp/idp_metadata_different_sign_and_encrypt_cert.xml"); + assertEquals("https://app.onelogin.com/saml/metadata/383123", idpInfo2.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://app.onelogin.com/trust/saml2/http-post/sso/383123", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo2.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals(Util.loadCert((String) idpInfo2.get(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY)), Util.loadCert( + "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==")); + assertEquals(Util.loadCert((String)idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "0")), Util.loadCert( + "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=")); + assertNull(idpInfo2.get(SettingsBuilder.IDP_X509CERTMULTI_PROPERTY_KEY + "." + "2")); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", idpInfo2.get(SettingsBuilder.SP_NAMEIDFORMAT_PROPERTY_KEY)); + } + + @Test + public void testParseSeparateSingleLogoutServiceResponseLocation() throws Exception { + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/metadata_slo_responselocation.xml"); + assertEquals("https://idp.examle.com/saml/metadata", idpInfo.get(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sso", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/slo", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY)); + assertEquals("https://idp.examle.com/saml/sloresp", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_RESPONSE_URL_PROPERTY_KEY)); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", idpInfo.get(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY)); + } + + @Test + public void testInjectIntoSettings() throws Exception { + Saml2Settings setting = new SettingsBuilder().fromFile("config/config.all.properties").build(); + + assertEquals("http://idp.example.com/", setting.getIdpEntityId()); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting.getIdpx509cert()); + assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); + assertEquals(4, setting.getContacts().size()); + assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); + assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); + assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); + assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); + assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); + assertEquals("SP Java", setting.getOrganization().getOrgName()); + assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); + + Map idpInfo = IdPMetadataParser.parseFileXML("data/metadata/idp/FederationMetadata.xml"); + setting = IdPMetadataParser.injectIntoSettings(setting, idpInfo); + assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting.getIdpEntityId()); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(setting.getIdpx509cert(), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); + assertEquals(setting.getIdpx509certMulti().get(0), Util.loadCert( + "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); + assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + assertEquals(Constants.RSA_SHA512, setting.getSignatureAlgorithm()); + assertEquals(Constants.SHA512, setting.getDigestAlgorithm()); + assertEquals(4, setting.getContacts().size()); + assertEquals("administrative@example.com", setting.getContacts().get(0).getEmailAddresses().get(0)); + assertEquals("administrative2@example.com", setting.getContacts().get(0).getEmailAddresses().get(1)); + assertEquals("info@example.com", setting.getContacts().get(1).getEmailAddresses().get(0)); + assertEquals("technical@example.com", setting.getContacts().get(2).getEmailAddresses().get(0)); + assertEquals("support@example.com", setting.getContacts().get(3).getEmailAddresses().get(0)); + assertEquals("SP Java", setting.getOrganization().getOrgName()); + assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); + + Saml2Settings setting2 = new SettingsBuilder().fromFile("config/config.min.properties").build(); + assertEquals("http://idp.example.com/", setting2.getIdpEntityId()); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting2.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php", setting2.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(Util.loadCert(Util.getFileAsString("certs/certificate1")), setting2.getIdpx509cert()); + assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); + assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); + assertEquals(0, setting2.getContacts().size()); + assertNull(setting2.getOrganization()); + assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); + + setting2 = IdPMetadataParser.injectIntoSettings(setting2, idpInfo); + assertEquals("http://idp.adfs.example.com/adfs/services/trust", setting2.getIdpEntityId()); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleSignOnServiceUrl().toString()); + assertEquals(setting2.getIdpSingleSignOnServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals("https://idp.adfs.example.com/adfs/ls/", setting2.getIdpSingleLogoutServiceUrl().toString()); + assertEquals(setting2.getIdpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + assertEquals(setting2.getIdpx509cert(), Util.loadCert( + "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxMjI3NTFaFw0yNzA0MTMxMjI3NTFaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYtEZ7hGZiNp+NecbcQXosYl8TzVOdL44b3Nl+BxL26Bvnt8YNnE63xiQzo7xDdO6+1MWWO26mMxwMpooTToOJgrot9YhlIX1VHIUPbOEGczSmXzCCmMhS26vR/leoLNah8QqCF1UdCoNQejb0fDCy+Q1yEdMXYkBWsFGfDSHSSQIDAQABo1AwTjAdBgNVHQ4EFgQUT1g33aGN0f6BJPgpYbr1pHrMZrYwHwYDVR0jBBgwFoAUT1g33aGN0f6BJPgpYbr1pHrMZrYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQB6233Ic9bb6OCMT6hE1mRzhoP+AbixeojtUuM1IUG4JI5YUGsjsym96VBw+/ciwDLuxNYg6ZWu++WxWNwF3LwVRZGQ8bDdxYldm6VorvIbps2tzyT5N32xgMAgzy/3SZf6YOihdotXJd5AZNVp/razVO17WrjsFvldAlKtk0SM7w==")); + assertEquals(setting2.getIdpx509certMulti().get(0), Util.loadCert( + "MIIC9jCCAd6gAwIBAgIQI/B8CLE676pCR2/QaKih9TANBgkqhkiG9w0BAQsFADA3MTUwMwYDVQQDEyxBREZTIFNpZ25pbmcgLSBsb2dpbnRlc3Qub3dlbnNib3JvaGVhbHRoLm9yZzAeFw0xNjEwMjUxNjI4MzhaFw0xNzEwMjUxNjI4MzhaMDcxNTAzBgNVBAMTLEFERlMgU2lnbmluZyAtIGxvZ2ludGVzdC5vd2Vuc2Jvcm9oZWFsdGgub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjikmKRRVD5oK3fxm0xNfDqvWCujZIhtv2zeIwmoRKUAjo6KeUhauII4BHh5DclmbOFD4ruli3sNWGKgqVCX1AFW/p3m3/FtzeumFeZSmyfqeJEeOqAK5jAom/MfXxaQ85QHlGa0BTtdWdCuxhJz5G797o4s1Me/8QOQdmbkkwOHOVXRDW0QxBXvsRB1jPpIO+JvNcWFpvJrELccD0Fws91LH42j2C4gDNR8JLu5LrUGL6zAIq8NM7wfbwoax9n/0tIZKa6lo6szpXGqiMrDBJPpAqC5MSePyp5/SEX6jxwodQUGRgI5bKILQwOWDrkgfsK1MIeHfovtyqnDZj8e9VwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBKbK4qu7WTLYeQW7OcFAeWcT5D7ujo61QtPf+6eY8hpNntN8yF71vGm+5zdOjmw18igxUrf3W7dLk2wAogXK196WX34x9muorwmFK/HqmKuy0kWWzGcNzZHb0o4Md2Ux7QQVoHqD6dUSqUisOBs34ZPgT5R42LepJTGDEZSkvOxUv9V6fY5dYk8UaWbZ7MQAFi1CnOyybq2nVNjpuxWyJ6SsHQYKRhXa7XGurXFB2mlgcjVj9jxW0gO7djkgRD68b6PNpQmJkbKnkCtJg9YsSeOmuUjwgh4DlcIo5jZocKd5bnLbQ9XKJ3YQHRxFoZbP3BXKrfhVV3vqqzRxMwjZmK")); + assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); + assertEquals(Constants.RSA_SHA1, setting2.getSignatureAlgorithm()); + assertEquals(Constants.SHA1, setting2.getDigestAlgorithm()); + assertEquals(0, setting2.getContacts().size()); + assertNull(setting2.getOrganization()); + assertEquals(Util.UNIQUE_ID_PREFIX, setting2.getUniqueIDPrefix()); + } + } \ No newline at end of file