diff --git a/README.md b/README.md index df0c3bf0..14b51a80 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,18 @@ onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-sa # HTTP-POST binding only onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST +# The above two settings declare just one Assertion Consumer Service (ACS). As an alternative, it's also +# possible to specify multiple Assertion Consumer Services by providing indexed properties: the index +# is used as the ACS index as well and one of the defined services may be marked as the default one. +# Please note that, when indexed ACS properties are present, the non-indexed ones are ignored. +# Here is a complete example, but remember that Onelogin Toolkit still actually supports HTTP-POST binding +# only for response processing: +#onelogin.saml2.sp.assertion_consumer_service[0].url = http://localhost:8081/java-saml-jspsample/acs1.jsp +#onelogin.saml2.sp.assertion_consumer_service[0].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect +#onelogin.saml2.sp.assertion_consumer_service[1].url = http://localhost:8081/java-saml-jspsample/acs2.jsp +#onelogin.saml2.sp.assertion_consumer_service[1].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST +#onelogin.saml2.sp.assertion_consumer_service[1].default = true + # Specifies info about where and how the message MUST be # returned to the requester, in this case our SP. onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-tookit-jspsample/sls.jsp diff --git a/core/src/main/java/com/onelogin/saml2/authn/AssertionConsumerServiceSelector.java b/core/src/main/java/com/onelogin/saml2/authn/AssertionConsumerServiceSelector.java new file mode 100644 index 00000000..413618fb --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/authn/AssertionConsumerServiceSelector.java @@ -0,0 +1,168 @@ +package com.onelogin.saml2.authn; + +import java.net.URL; + +import com.onelogin.saml2.model.AssertionConsumerService; +import com.onelogin.saml2.settings.Saml2Settings; + +/** + * Interfaced used to select the Assertion Consumer Service (ACS) to be + * specified in an authentication request. An instance of this interface can be + * passed as an input parameter in a {@link AuthnRequestParams} to be used when + * initiating a login operation. + *

+ * A set of predefined implementations are provided: they should cover the most + * common cases. + */ +@FunctionalInterface +public interface AssertionConsumerServiceSelector { + + /** + * Simple class holding data used to select an Assertion Consumer Service (ACS) + * within an authentication request. + *

+ * The index, if specified, has priority over the pair URL/protocol binding. + */ + static class AssertionConsumerServiceSelection { + /** Assertion Consumer Service index. */ + public final Integer index; + /** Assertion Consumer Service URL. */ + public final URL url; + /** Assertion Consumer Service protocol binding. */ + public final String protocolBinding; + + /** + * Creates an Assertion Consumer Service selection by index. + * + * @param index + * the ACS index + */ + public AssertionConsumerServiceSelection(final int index) { + this.index = index; + this.url = null; + this.protocolBinding = null; + } + + /** + * Creates an Assertion Consumer Service selection by URL and protocol binding. + * + * @param url + * the ACS URL + * @param protocolBinding + * the ACS protocol binding + */ + public AssertionConsumerServiceSelection(final URL url, final String protocolBinding) { + this.index = null; + this.url = url; + this.protocolBinding = protocolBinding; + } + } + + /** + * @return a selector that will cause the authentication request not to specify + * any Assertion Consumer Service, letting the IdP determine which is + * the default one; if the agreement between the SP and the IdP to map + * Assertion Consumer Services is based on metadata, it means that the + * IdP is expected to select the ACS marked there as being the default + * one (or the only declared ACS, if just one exists and hopefully not + * explicitly set as not being the default one...); + * indeed, in sane cases the final selection result is expected to be + * the same the one provided by + * {@link AssertionConsumerServiceSelector#useDefaultByIndex(Saml2Settings)} + * and + * {@link AssertionConsumerServiceSelector#useDefaultByUrlAndBinding(Saml2Settings)}, + * with those two however causing an explicit indication of the choice + * being made by the SP in the authentication request, indication that + * the IdP must then respect + */ + static AssertionConsumerServiceSelector useImplicitDefault() { + return () -> null; + } + + /** + * @param settings + * the SAML settings, containing the list of the available + * Assertion Consumer Services (see + * {@link Saml2Settings#getSpAssertionConsumerServices()}) + * @return a selector that will cause the authentication request to explicitly + * specify the default Assertion Consumer Service declared in a set of + * SAML settings, selecting it by index; if no default ACS could be + * unambiguously detected, this falls back to + * {@link #useImplicitDefault()} + * @see Saml2Settings#getSpAssertionConsumerServices() + * @see Saml2Settings#getSpDefaultAssertionConsumerService() + */ + static AssertionConsumerServiceSelector useDefaultByIndex(final Saml2Settings settings) { + return settings.getSpDefaultAssertionConsumerService().map(AssertionConsumerServiceSelector::byIndex) + .orElse(useImplicitDefault()); + } + + /** + * @param settings + * the SAML settings, containing the list of the available + * Assertion Consumer Services (see + * {@link Saml2Settings#getSpAssertionConsumerServices()}) + * @return a selector that will cause the authentication request to explicitly + * specify the default Assertion Consumer Service declared in a set of + * SAML settings, selecting it by URL and protocol binding; if no + * default ACS could be unambiguously detected, this falls back to + * {@link #useImplicitDefault()} + * @see Saml2Settings#getSpAssertionConsumerServices() + * @see Saml2Settings#getSpDefaultAssertionConsumerService() + */ + static AssertionConsumerServiceSelector useDefaultByUrlAndBinding(final Saml2Settings settings) { + return settings.getSpDefaultAssertionConsumerService().map(AssertionConsumerServiceSelector::byUrlAndBinding) + .orElse(useImplicitDefault()); + } + + /** + * @param assertionConsumerService + * the Assertion Consumer Service to select + * @return a selector that chooses the specified Assertion Consumer Service by + * index + */ + static AssertionConsumerServiceSelector byIndex(final AssertionConsumerService assertionConsumerService) { + return byIndex(assertionConsumerService.getIndex()); + } + + /** + * @param assertionConsumerService + * the Assertion Consumer Service to select + * @return a selector that chooses the specified Assertion Consumer Service by + * location URL and protocol binding + */ + static AssertionConsumerServiceSelector byUrlAndBinding(final AssertionConsumerService assertionConsumerService) { + return () -> new AssertionConsumerServiceSelection(assertionConsumerService.getLocation(), + assertionConsumerService.getBinding()); + } + + /** + * @param index + * the index of the Assertion Consumer Service to select + * @return a selector that chooses the Assertion Consumer Service with the given + * index + */ + static AssertionConsumerServiceSelector byIndex(final int index) { + return () -> new AssertionConsumerServiceSelection(index); + } + + /** + * @param url + * the URL of the Assertion Consumer Service to select + * @param protocolBinding + * the protocol binding of the Assertion Consumer Service to select + * @return a selector that chooses the Assertion Consumer Service with the given + * URL and protocol binding + */ + static AssertionConsumerServiceSelector byUrlAndBinding(final URL url, final String protocolBinding) { + return () -> new AssertionConsumerServiceSelection(url, protocolBinding); + } + + /** + * Returns a description of the selected Assertion Consumer Service. + * + * @return the service index, or null if the default one should be + * selected + */ + AssertionConsumerServiceSelection getAssertionConsumerServiceSelection(); +} \ No newline at end of file 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..808c6d61 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -11,6 +11,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.onelogin.saml2.authn.AssertionConsumerServiceSelector.AssertionConsumerServiceSelection; +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.Organization; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.util.Constants; @@ -250,8 +252,6 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti String issueInstantString = Util.formatDateTime(issueInstant.getTimeInMillis()); valueMap.put("issueInstant", issueInstantString); 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 = ""; @@ -266,6 +266,37 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti } valueMap.put("requestedAuthnContextStr", requestedAuthnContextStr); + + String assertionConsumerServiceSelectionStr = ""; + AssertionConsumerServiceSelection acsSelection = params.getAssertionConsumerServiceSelector() + .getAssertionConsumerServiceSelection(); + List spAssertionConsumerServices = settings.getSpAssertionConsumerServices(); + if (spAssertionConsumerServices.size() == 1) { + /* + * For backward compatibility: if an implicit default ACS selection is + * requested, just one single ACS is defined in the settings, it has index 1 + * (which was the default index used before introducing multi ACS support) and + * no explicit default status (as it was before introducing multi ACS support), + * then select that ACS by using its URL and protocol binding: indeed, the old + * way to specify the ACS in the AuhtnRequest was just this. The selected ACS + * should be the same anyway, we just ensure that, in this way, the produced + * AuthnRequest is exactly the same as it was before introducing multi ACS + * support. + */ + final AssertionConsumerService acs = spAssertionConsumerServices.get(0); + if (acsSelection == null && acs.getIndex() == 1 && acs.isDefault() == null) + acsSelection = AssertionConsumerServiceSelector.byUrlAndBinding(acs) + .getAssertionConsumerServiceSelection(); + } + if (acsSelection != null) { + if (acsSelection.index != null) + assertionConsumerServiceSelectionStr = " AssertionConsumerServiceIndex=\"" + acsSelection.index + + "\""; + else + assertionConsumerServiceSelectionStr = " ProtocolBinding=\"" + Util.toXml(acsSelection.protocolBinding) + + "\" AssertionConsumerServiceURL=\"" + Util.toXml(String.valueOf(acsSelection.url)) + "\""; + } + valueMap.put("assertionConsumerServiceSelection", assertionConsumerServiceSelectionStr); return new StrSubstitutor(valueMap); } @@ -275,7 +306,7 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti */ private static StringBuilder getAuthnRequestTemplate() { StringBuilder template = new StringBuilder(); - template.append(""); + template.append(""); template.append("${spEntityid}"); template.append("${subjectStr}${nameIDPolicyStr}${requestedAuthnContextStr}"); return template; 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 69954254..3ac52ea9 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java @@ -27,6 +27,12 @@ public class AuthnRequestParams { */ private final String nameIdValueReq; + /* + * Selector to use to specify the Assertion Consumer Service that will consume + * the response + */ + private final AssertionConsumerServiceSelector assertionConsumerServiceSelector; + /** * Create a set of authentication request input parameters. * @@ -64,6 +70,27 @@ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setName this(forceAuthn, isPassive, setNameIdPolicy, allowCreate, 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 assertionConsumerServiceSelector + * the selector to use to specify the Assertion Consumer Service + * that will consume the response; if null, + * {@link AssertionConsumerServiceSelector#useImplicitDefault()} is used + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, + AssertionConsumerServiceSelector assertionConsumerServiceSelector) { + this(forceAuthn, isPassive, setNameIdPolicy, true, null, assertionConsumerServiceSelector); + } + /** * Create a set of authentication request input parameters. * @@ -89,7 +116,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 @@ -103,11 +130,42 @@ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setName */ public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, boolean allowCreate, String nameIdValueReq) { + this(forceAuthn, isPassive, setNameIdPolicy, allowCreate, nameIdValueReq, 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 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 + * @param assertionConsumerServiceSelector + * the selector to use to specify the Assertion Consumer Service + * that will consume the response; if null, + * {@link AssertionConsumerServiceSelector#useImplicitDefault()} is used + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, boolean allowCreate, String nameIdValueReq, + AssertionConsumerServiceSelector assertionConsumerServiceSelector) { this.forceAuthn = forceAuthn; this.isPassive = isPassive; this.setNameIdPolicy = setNameIdPolicy; this.allowCreate = allowCreate; this.nameIdValueReq = nameIdValueReq; + this.assertionConsumerServiceSelector = assertionConsumerServiceSelector != null + ? assertionConsumerServiceSelector + : AssertionConsumerServiceSelector.useImplicitDefault(); } /** @@ -123,6 +181,7 @@ protected AuthnRequestParams(AuthnRequestParams source) { this.setNameIdPolicy = source.isSetNameIdPolicy(); this.allowCreate = source.isAllowCreate(); this.nameIdValueReq = source.getNameIdValueReq(); + this.assertionConsumerServiceSelector = source.getAssertionConsumerServiceSelector(); } /** @@ -163,4 +222,12 @@ public boolean isAllowCreate() { public String getNameIdValueReq() { return nameIdValueReq; } + + /** + * @return the selector to use to specify the Assertion Consumer Service that + * will consume the response + */ + public AssertionConsumerServiceSelector getAssertionConsumerServiceSelector() { + return assertionConsumerServiceSelector; + } } \ No newline at end of file diff --git a/core/src/main/java/com/onelogin/saml2/model/AssertionConsumerService.java b/core/src/main/java/com/onelogin/saml2/model/AssertionConsumerService.java new file mode 100644 index 00000000..e761b92c --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/model/AssertionConsumerService.java @@ -0,0 +1,137 @@ +package com.onelogin.saml2.model; + +import java.net.URL; + +import com.onelogin.saml2.util.Constants; + +/** + * AssertionConsumerService class of OneLogin's Java Toolkit. + * + * A class that stores an AssertionConsumerService (ACS) + */ +public class AssertionConsumerService { + + /** + * Service Index + */ + private final int index; + + /** + * Whether this service is the default one + */ + private final Boolean isDefault; + + /** + * Binding + */ + private final String binding; + + /** + * Location + */ + private final URL location; + + /** + * Constructor. + *

+ * {@link Constants#BINDING_HTTP_POST} binding will be set. + * + * @param location + * ACS location URL + */ + public AssertionConsumerService(final URL location) { + this(1, null, null, location); + } + + /** + * Constructor + * + * @param binding + * ACS Binding; if null, + * {@link Constants#BINDING_HTTP_POST} will be set + * @param location + * ACS location URL + */ + public AssertionConsumerService(final String binding, final URL location) { + this(1, null, binding, location); + } + + /** + * Constructor +* *

+ * {@link Constants#BINDING_HTTP_POST} binding will be set. + + * @param index + * ACS index + * @param location + * ACS location URL + */ + public AssertionConsumerService(final int index, final URL location) { + this(index, null, null, location); + } + + /** + * Constructor + *

+ * {@link Constants#BINDING_HTTP_POST} binding will be set. + * + * @param index + * ACS index + * @param isDefault + * Whether it's the default attribute consuming service + * @param location + * ACS location URL + */ + public AssertionConsumerService(final int index, final Boolean isDefault, final URL location) { + this(index, isDefault, null, location); + } + + /** + * Constructor + * + * @param index + * ACS index + * @param isDefault + * Whether it's the default attribute consuming service + * @param binding + * ACS Binding; if null, + * {@link Constants#BINDING_HTTP_POST} will be set + * @param location + * ACS location URL + */ + public AssertionConsumerService(final int index, final Boolean isDefault, final String binding, + final URL location) { + this.index = index; + this.isDefault = isDefault; + this.binding = binding != null ? binding : Constants.BINDING_HTTP_POST; + this.location = location; + } + + /** + * @return the ACS index + */ + public final int getIndex() { + return index; + } + + /** + * @return whether this is the default ACS + */ + public final Boolean isDefault() { + return isDefault; + } + + /** + * @return the binding + */ + public final String getBinding() { + return binding; + } + + /** + * @return the location + */ + public final URL getLocation() { + return location; + } +} \ 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 30a83184..eb6dc9d5 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -22,6 +22,7 @@ import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.Organization; +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.AttributeConsumingService; import com.onelogin.saml2.model.RequestedAttribute; import com.onelogin.saml2.util.Constants; @@ -165,8 +166,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif valueMap.put("strAuthnsign", String.valueOf(settings.getAuthnRequestsSigned())); valueMap.put("strWsign", String.valueOf(settings.getWantAssertionsSigned())); valueMap.put("spNameIDFormat", Util.toXml(settings.getSpNameIDFormat())); - valueMap.put("spAssertionConsumerServiceBinding", Util.toXml(settings.getSpAssertionConsumerServiceBinding())); - valueMap.put("spAssertionConsumerServiceUrl", Util.toXml(settings.getSpAssertionConsumerServiceUrl().toString())); + valueMap.put("strAssertionConsumerServices", toAssertionConsumerServicesXml(settings.getSpAssertionConsumerServices())); valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); valueMap.put("strAttributeConsumingService", getAttributeConsumingServiceXml()); @@ -194,9 +194,7 @@ private static StringBuilder getMetadataTemplate() { template.append(""); template.append("${strKeyDescriptor}"); template.append("${sls}${spNameIDFormat}"); - template.append(""); + template.append("${strAssertionConsumerServices}"); template.append("${strAttributeConsumingService}"); template.append("${strOrganization}${strContacts}"); template.append(""); @@ -204,6 +202,43 @@ private static StringBuilder getMetadataTemplate() { return template; } + /** + * Generates the AssertionConsumerService sections of the metadata's template + * + * @param assertionConsumerServices + * a list containing the Assertion Consumer Services to generate + * the metadata for + * @return the AssertionConsumerService sections of the metadata's template + */ + private String toAssertionConsumerServicesXml(List assertionConsumerServices) { + final StringBuilder acssXml = new StringBuilder(); + assertionConsumerServices.forEach(service -> acssXml.append(toAssertionConsumerServiceXml(service))); + return acssXml.toString(); + } + + /** + * Generates a single Attribute Consuming Service metadata fragment + * + * @param service + * the Attribute Consuming Service for which the XML fragment + * should be generated + * @return the generated XML fragment + */ + private String toAssertionConsumerServiceXml(AssertionConsumerService service) { + int index = service.getIndex(); + Boolean isDefault = service.isDefault(); + String binding = service.getBinding(); + URL location = service.getLocation(); + StringBuilder acsXml = new StringBuilder(); + acsXml.append(""); + return acsXml.toString(); + } + /** * Generates the AttributeConsumingService section of the metadata's template * 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..92b6d780 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -8,7 +8,9 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import com.onelogin.saml2.model.hsm.HSM; @@ -17,6 +19,8 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; + +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.Organization; import com.onelogin.saml2.util.Constants; @@ -40,8 +44,7 @@ public class Saml2Settings { // SP private String spEntityId = ""; - private URL spAssertionConsumerServiceUrl = null; - private String spAssertionConsumerServiceBinding = Constants.BINDING_HTTP_POST; + private List spAssertionConsumerServices = new ArrayList<>(); private URL spSingleLogoutServiceUrl = null; private String spSingleLogoutServiceBinding = Constants.BINDING_HTTP_REDIRECT; private String spNameIDFormat = Constants.NAMEID_UNSPECIFIED; @@ -112,17 +115,78 @@ public final String getSpEntityId() { } /** - * @return the spAssertionConsumerServiceUrl + * @return the SP Assertion Consumer Services + */ + public final List getSpAssertionConsumerServices() { + return spAssertionConsumerServices; + } + + /** + * Returns the default Assertion Consumer Service (ACS) within the list of + * configured Assertion Consumer Services, if one could be determined. + *

+ * The default ACS is determined in this way: + *

    + *
  1. if just one ACS is configured, it's the default one, unless it has + * {@link AssertionConsumerService#isDefault()} explicitly set + * to false + *
  2. if more than one ACS is configured, the one being the only one having + * {@link AssertionConsumerService#isDefault()} set to true is the + * default one + *
+ * In all the other (unusual) cases, a default ACS cannot be unambiguously + * detected. + * + * @return an {@link Optional} containing the default Assertion Consumer + * Service, if one could be unambiguously detected, an empty + * {@link Optional} otherwise + * @see #getSpAssertionConsumerServices() + */ + public final Optional getSpDefaultAssertionConsumerService() { + if(spAssertionConsumerServices.size() == 1) { + final AssertionConsumerService firstAcs = spAssertionConsumerServices.get(0); + if(Boolean.FALSE.equals(firstAcs.isDefault())) + return Optional.empty(); + else + return Optional.of(firstAcs); + } + final List allDefaults = spAssertionConsumerServices.stream() + .filter(acs -> Boolean.TRUE.equals(acs.isDefault())).collect(Collectors.toList()); + if (allDefaults.size() == 1) + return Optional.of(allDefaults.get(0)); + else + return Optional.empty(); + } + + /** + * @return the location of the first Assertion Consumer Service + * @deprecated use {@link #getSpAssertionConsumerServices()} to retrieve the + * configured Assertion Consumer Services; this returns the location + * of the first configured ACS, or null if no ACS is + * currently defined */ + @Deprecated public final URL getSpAssertionConsumerServiceUrl() { - return spAssertionConsumerServiceUrl; + if(spAssertionConsumerServices.isEmpty()) + return null; + else + return spAssertionConsumerServices.get(0).getLocation(); } /** - * @return the spAssertionConsumerServiceBinding setting value + * @return the binding of the first Assertion Consumer Service + * @deprecated use {@link #getSpAssertionConsumerServices()} to retrieve the + * configured Assertion Consumer Services; this returns the binding + * of the first configured ACS, but for backward compatibility it + * returns {@link Constants#BINDING_HTTP_POST} if no ACS is + * currently defined */ + @Deprecated public final String getSpAssertionConsumerServiceBinding() { - return spAssertionConsumerServiceBinding; + if(spAssertionConsumerServices.isEmpty()) + return Constants.BINDING_HTTP_POST; // for backward compatibility + else + return spAssertionConsumerServices.get(0).getBinding(); } /** @@ -435,23 +499,53 @@ protected final void setSpEntityId(String spEntityId) { } /** - * Set the spAssertionConsumerServiceUrl setting value + * Set the Assertion Consumer Services to be exposed by the Service Provider + * + * @param spAssertionConsumerServices + * the Assertion Consumer Services to set + */ + protected void setSpAssertionConsumerServices(List spAssertionConsumerServices) { + this.spAssertionConsumerServices = spAssertionConsumerServices; + } + + /** + * Set the location URL on the first defined Assertion Consumer Service * * @param spAssertionConsumerServiceUrl - * the spAssertionConsumerServiceUrl value to be set + * the spAssertionConsumerServiceUrl value to be set + * @deprecated use {@link #setSpAssertionConsumerServices(List)} to set up one + * (or more) Assertion Consumer Services; if no ACS is currently + * defined, a new one will be created with the specified location + * and a {@link Constants#BINDING_HTTP_POST} binding */ + @Deprecated protected final void setSpAssertionConsumerServiceUrl(URL spAssertionConsumerServiceUrl) { - this.spAssertionConsumerServiceUrl = spAssertionConsumerServiceUrl; + if(spAssertionConsumerServices.isEmpty()) { + spAssertionConsumerServices.add(new AssertionConsumerService(spAssertionConsumerServiceUrl)); + } else { + AssertionConsumerService firstAcs = spAssertionConsumerServices.get(0); + spAssertionConsumerServices.set(0, new AssertionConsumerService(firstAcs.getIndex(), firstAcs.isDefault(), firstAcs.getBinding(), spAssertionConsumerServiceUrl)); + } } /** - * Set the spAssertionConsumerServiceBinding setting value + * Set the binding on the first defined Assertion Consumer Service * * @param spAssertionConsumerServiceBinding - * the spAssertionConsumerServiceBinding value to be set + * the spAssertionConsumerServiceBinding value to be set + * @deprecated use {@link #setSpAssertionConsumerServices(List)} to set up one + * (or more) Assertion Consumer Services; if no ACS is currently + * defined, a new one will be created with the specified binding and + * no location */ + @Deprecated protected final void setSpAssertionConsumerServiceBinding(String spAssertionConsumerServiceBinding) { - this.spAssertionConsumerServiceBinding = spAssertionConsumerServiceBinding; + if(spAssertionConsumerServices.isEmpty()) { + spAssertionConsumerServices.add(new AssertionConsumerService(spAssertionConsumerServiceBinding, null)); + } else { + AssertionConsumerService firstAcs = spAssertionConsumerServices.get(0); + spAssertionConsumerServices.set(0, new AssertionConsumerService(firstAcs.getIndex(), firstAcs.isDefault(), spAssertionConsumerServiceBinding, firstAcs.getLocation())); + } } /** @@ -988,6 +1082,40 @@ private boolean checkIdpx509certRequired () { return this.getIdpx509certMulti() != null && !this.getIdpx509certMulti().isEmpty(); } + + /* + * Auxiliary method to check Assertion Consumer Services are properly + * configured. + * + * @param errors the list to add to when an error is encountered + */ + private void checkAssertionConsumerServices(List errors) { + String errorMsg; + // at least one ACS + /* + * for backward compatibility, use error sp_acs_not_found even when there's 1 + * ACS but with no location. + */ + List assertionConsumerServices = getSpAssertionConsumerServices(); + if (assertionConsumerServices.size() == 0 || (assertionConsumerServices.size() == 1 + && assertionConsumerServices.get(0).getLocation() == null)) { + errorMsg = "sp_acs_not_found"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + // all ACSs must have a location + if(assertionConsumerServices.stream().anyMatch(acs -> acs.getLocation() == null)) { + errorMsg = "sp_acs_not_enough_data"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + // there must be at most one with default = true + if(assertionConsumerServices.stream().filter(acs -> Boolean.TRUE.equals(acs.isDefault())).count() > 1) { + errorMsg = "sp_acs_multiple_defaults"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + } /** * Checks the SP settings . @@ -1004,11 +1132,7 @@ public List checkSPSettings() { LOGGER.error(errorMsg); } - if (!checkRequired(getSpAssertionConsumerServiceUrl())) { - errorMsg = "sp_acs_not_found"; - errors.add(errorMsg); - LOGGER.error(errorMsg); - } + checkAssertionConsumerServices(errors); if (this.getHsm() == null && (this.getAuthnRequestsSigned() || this.getLogoutRequestSigned() || this.getLogoutResponseSigned() || this.getWantAssertionsEncrypted() || this.getWantNameIdEncrypted()) && !this.checkSPCerts()) { 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..07ca0794 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.onelogin.saml2.exception.Error; +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.model.Organization; @@ -64,7 +65,32 @@ public class SettingsBuilder { // SP public final static String SP_ENTITYID_PROPERTY_KEY = "onelogin.saml2.sp.entityid"; + public final static String SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX = "onelogin.saml2.sp.assertion_consumer_service"; + public final static String SP_ASSERTION_CONSUMER_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX = "default"; + public final static String SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX = "url"; + public final static String SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX = "binding"; + + /** + * @deprecated the use of this property key will still work to retrieve the + * location of the Assertion Consumer Service if just one is defined + * with no index; however, + * {@link #SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX} and + * {@link #SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX} + * should be used to properly retrieve the location of any (indexed + * or non-indexed) defined Assertion Consumer Service + */ + @Deprecated public final static String SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY = "onelogin.saml2.sp.assertion_consumer_service.url"; + /** + * @deprecated the use of this property key will still work to retrieve the + * binding of the Assertion Consumer Service if just one is defined + * with no index; however, + * {@link #SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX} and + * {@link #SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX} + * should be used to properly retrieve the binding of any (indexed + * or non-indexed) defined Assertion Consumer Service + */ + @Deprecated public final static String SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY = "onelogin.saml2.sp.assertion_consumer_service.binding"; 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"; @@ -544,6 +570,52 @@ private Contact loadContact(Map contactProps, int index) { return new Contact(contactType, company, givenName, surName, emails, numbers); } + /** + * Loads the Assertion Consumer Services from settings. + * + * @return a list containing the loaded Assertion Consumer Services + */ + private List loadAssertionConsumerServices() { + // first split properties into a map of properties + // key = service index; value = service properties + final SortedMap> acsProps = + extractIndexedProperties(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX, samlData); + // then build each Assertion Consumer Service + if(acsProps.containsKey(-1) && acsProps.size() == 1) + // single service specified; use index 1 for backward compatibility + return Arrays.asList(loadAssertionConsumerService(acsProps.get(-1), 1)); + else + // multiple indexed services specified + return acsProps.entrySet().stream() + // ignore non-indexed service + .filter(entry -> { + final boolean indexed = entry.getKey() != -1; + if(!indexed) { + LOGGER.warn("non indexed Assertion Consumer Service found along with other indexed Services; the non-indexed one will be ignored"); + } + return indexed; + }) + .map(entry -> loadAssertionConsumerService(entry.getValue(), entry.getKey())) + .collect(Collectors.toList()); + } + + /** + * Loads a single Assertion Consumer Service from settings. + * + * @param acsProps + * a map containing the Assertion Consumer Service settings + * @param index + * the index to be set on the returned Assertion Consumer Service + * @return the loaded Assertion Consumer Service + */ + private AssertionConsumerService loadAssertionConsumerService(Map acsProps, int index) { + final String binding = loadStringProperty(SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, acsProps); + final URL location = loadURLProperty(SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, acsProps); + final Boolean isDefault = loadBooleanProperty(SP_ASSERTION_CONSUMER_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX, acsProps); + final AssertionConsumerService acs = new AssertionConsumerService(index, isDefault, binding, location); + return acs; + } + /** * 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 @@ -708,16 +780,8 @@ private void loadSpSetting() { saml2Setting.setSpEntityId(spEntityID); } - URL assertionConsumerServiceUrl = loadURLProperty(SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY); - if (assertionConsumerServiceUrl != null) { - saml2Setting.setSpAssertionConsumerServiceUrl(assertionConsumerServiceUrl); - } - - String spAssertionConsumerServiceBinding = loadStringProperty(SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY); - if (spAssertionConsumerServiceBinding != null) { - saml2Setting.setSpAssertionConsumerServiceBinding(spAssertionConsumerServiceBinding); - } - + saml2Setting.setSpAssertionConsumerServices(loadAssertionConsumerServices()); + URL spSingleLogoutServiceUrl = loadURLProperty(SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY); if (spSingleLogoutServiceUrl != null) { saml2Setting.setSpSingleLogoutServiceUrl(spSingleLogoutServiceUrl); @@ -799,7 +863,19 @@ private String loadStringProperty(String propertyKey, Map data) * @return the value */ private Boolean loadBooleanProperty(String propertyKey) { - Object propValue = samlData.get(propertyKey); + return loadBooleanProperty(propertyKey, samlData); + } + + /** + * Loads a property of the type Boolean from the specified data + * + * @param propertyKey the property name + * @param data the input data + * + * @return the value + */ + private Boolean loadBooleanProperty(String propertyKey, Map data) { + Object propValue = data.get(propertyKey); if (isString(propValue)) { return Boolean.parseBoolean(((String) propValue).trim()); } @@ -841,8 +917,19 @@ private List loadListProperty(String propertyKey) { * @return the value */ private URL loadURLProperty(String propertyKey) { + return loadURLProperty(propertyKey, samlData); + } - Object propValue = samlData.get(propertyKey); + /** + * Loads a property of the type URL from the specified data + * + * @param propertyKey the property name + * @param data the input data + * + * @return the value + */ + private URL loadURLProperty(String propertyKey, Map data) { + Object propValue = data.get(propertyKey); if (isString(propValue)) { 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 20886fb8..f2c873da 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 @@ -9,13 +9,14 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import java.net.URL; 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.AssertionConsumerServiceSelector; import com.onelogin.saml2.authn.AuthnRequest; import com.onelogin.saml2.authn.AuthnRequestParams; import com.onelogin.saml2.settings.Saml2Settings; @@ -122,7 +123,7 @@ public void testGetEncodedAuthnRequestOnlySettingsSpecialChars() throws Exceptio String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); assertThat(authnRequestStr, containsString("")); } + /** + * Tests the AuthnRequest Constructor + * The creation of a deflated SAML Request with the index of the desired Assertion Consumer Service + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testAssertionConsumerServiceSelector() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + + // just one non-indexed ACS specified - backward compatible URL/binding ACS specification + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } + /** + * Tests the constructor method of Metadata + * + * @throws Exception + * @see com.onelogin.saml2.settings.Metadata + */ + @Test + public void testMetadataMultiAssertionConsumerServices() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_multi_assertion_consumer_services.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("")); + assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); + } + /** * Tests the constructor method of Metadata (Expiration) * 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 67bbf84b..bcaa9a79 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 @@ -5,11 +5,16 @@ import static org.hamcrest.CoreMatchers.not; 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 java.io.IOException; +import java.net.URL; import java.util.Calendar; +import java.util.Collections; import java.util.List; +import java.util.Optional; + import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -20,6 +25,7 @@ import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.SchemaFactory; import com.onelogin.saml2.util.Util; +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.hsm.AzureKeyVault; /** @@ -99,7 +105,7 @@ public void testCheckIdPSettingsOk() throws IOException, Error { /** * Tests the checkSPSettings method of the Saml2Settings - * Case: Check that all possible IdP errors are found + * Case: Check that all possible SP errors are found * * @throws IOException * @throws Error @@ -117,6 +123,17 @@ public void testCheckSPSettingsAllErrors() throws IOException, Error { assertThat(settingsErrors, hasItem("contact_type_invalid")); assertThat(settingsErrors, hasItem("contact_not_enough_data")); assertThat(settingsErrors, hasItem("organization_not_enough_data")); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.sperrors_multi_assertion_consumer_services.properties").build(); + List settings2Errors = settings2.checkSPSettings(); + assertFalse(settings2Errors.isEmpty()); + assertThat(settings2Errors, hasItem("sp_entityId_not_found")); + assertThat(settings2Errors, hasItem("sp_acs_not_enough_data")); + assertThat(settings2Errors, hasItem("sp_acs_multiple_defaults")); + assertThat(settings2Errors, hasItem("sp_cert_not_found_and_required")); + assertThat(settings2Errors, hasItem("contact_type_invalid")); + assertThat(settings2Errors, hasItem("contact_not_enough_data")); + assertThat(settings2Errors, hasItem("organization_not_enough_data")); } /** @@ -133,6 +150,10 @@ public void testCheckSPSettingsOk() throws IOException, Error { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); List settingsErrors = settings.checkSPSettings(); assertTrue(settingsErrors.isEmpty()); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.all_multi_assertion_consumer_services.properties").build(); + List settings2Errors = settings2.checkSPSettings(); + assertTrue(settings2Errors.isEmpty()); } /** @@ -159,6 +180,20 @@ public void testCheckSettingsAllErrors() 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")); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.allerrors_multi_assertion_consumer_services.properties").build(); + List settings2Errors = settings2.checkSettings(); + assertThat(settings2Errors, hasItem("sp_entityId_not_found")); + assertThat(settings2Errors, hasItem("sp_acs_not_enough_data")); + assertThat(settings2Errors, hasItem("sp_acs_multiple_defaults")); + assertThat(settings2Errors, hasItem("sp_cert_not_found_and_required")); + assertThat(settings2Errors, hasItem("contact_type_invalid")); + assertThat(settings2Errors, hasItem("contact_not_enough_data")); + assertThat(settings2Errors, hasItem("organization_not_enough_data")); + assertThat(settings2Errors, hasItem("idp_entityId_not_found")); + assertThat(settings2Errors, hasItem("idp_sso_url_invalid")); + assertThat(settings2Errors, hasItem("idp_cert_or_fingerprint_not_found_and_required")); + assertThat(settings2Errors, hasItem("idp_cert_not_found_and_required")); } /** @@ -214,6 +249,10 @@ public void testCheckSettingsOk() throws IOException, Error { Saml2Settings settings = new SettingsBuilder().fromFile("config/config.all.properties").build(); List settingsErrors = settings.checkSettings(); assertTrue(settingsErrors.isEmpty()); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.all_multi_assertion_consumer_services.properties").build(); + List settings2Errors = settings2.checkSPSettings(); + assertTrue(settings2Errors.isEmpty()); } /** @@ -287,6 +326,40 @@ public void testGetSPMetadataUnsigned() throws Exception { assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); } + /** + * Tests the getSPMetadata method of the Saml2Settings + *

+ * * Case Unsigned metadata with multiple Assertion Consumer Services + * + * @throws Exception + * + * @see com.onelogin.saml2.settings.Saml2Settings#getSPMetadata + */ + @Test + public void testGetSPMetadataUnsignedMultiAssertionConsumerServices() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_multi_assertion_consumer_services.properties").build(); + + String metadataStr = settings.getSPMetadata(); + + 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("")); + assertThat(metadataStr, containsString("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); + } + /** * Tests the getSPMetadata method of the Saml2Settings * * Case Unsigned metadata No SLS @@ -377,6 +450,12 @@ public void testValidateMetadataValid() throws Exception { List errors = Saml2Settings.validateMetadata(metadataStr); assertTrue(errors.isEmpty()); + + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.all_multi_assertion_consumer_services.properties").build(); + String metadataStr2 = settings2.getSPMetadata(); + + List errors2 = Saml2Settings.validateMetadata(metadataStr2); + assertTrue(errors2.isEmpty()); } /** @@ -491,4 +570,43 @@ public void testValidateMetadataExpired() throws Exception { assertFalse(errors.isEmpty()); assertTrue(errors.contains("expired_xml")); } + + /** + * Tests the getSpDefaultAssertionConsumerService method of the Saml2Settings + * + * @throws Exception + * + * @see com.onelogin.saml2.settings.Saml2Settings#getSpDefaultAssertionConsumerService() + */ + @Test + public void testGetSpDefaultAssertionConsumerService() throws Exception { + // just one ACS with no explicit "default" attribute + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min.properties").build(); + Optional defaultAcs = settings.getSpDefaultAssertionConsumerService(); + assertTrue(defaultAcs.isPresent()); + assertNull(defaultAcs.get().isDefault()); + assertEquals(settings.getSpAssertionConsumerServices().get(0), defaultAcs.get()); + // multiple ACSes with a default one + Saml2Settings settings2 = new SettingsBuilder().fromFile("config/config.min_multi_assertion_consumer_services.properties").build(); + defaultAcs = settings2.getSpDefaultAssertionConsumerService(); + assertTrue(defaultAcs.isPresent()); + assertTrue(defaultAcs.get().isDefault()); + assertEquals(settings2.getSpAssertionConsumerServices().get(1), defaultAcs.get()); + // multiple ACSes with multiple default ones + Saml2Settings settings3 = new SettingsBuilder().fromFile("config/config.sperrors_multi_assertion_consumer_services.properties").build(); + defaultAcs = settings3.getSpDefaultAssertionConsumerService(); + assertFalse(defaultAcs.isPresent()); + // no ACSses at all + Saml2Settings settings4 = new SettingsBuilder().fromFile("config/config.empty.properties").build(); + defaultAcs = settings4.getSpDefaultAssertionConsumerService(); + assertFalse(defaultAcs.isPresent()); + // edge case: just one ACS but with explicit default=false + settings.getSpAssertionConsumerServices().set(0, new AssertionConsumerService(1, false, new URL("http://www.example.com"))); + defaultAcs = settings.getSpDefaultAssertionConsumerService(); + assertFalse(defaultAcs.isPresent()); + // edge case: multiple ACS but no one with default=true + settings2.getSpAssertionConsumerServices().set(1, new AssertionConsumerService(1, false, new URL("http://www.example.com"))); + defaultAcs = settings2.getSpDefaultAssertionConsumerService(); + assertFalse(defaultAcs.isPresent()); + } } 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 b9206d4f..8a83ad91 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 @@ -33,6 +33,7 @@ import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.model.AssertionConsumerService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.model.Organization; @@ -130,6 +131,7 @@ public void testLoadFromFileEmpty() throws IOException, CertificateException, UR assertTrue(setting.isStrict()); assertTrue(setting.getSpEntityId().isEmpty()); + assertTrue(setting.getSpAssertionConsumerServices().isEmpty()); assertNull(setting.getSpAssertionConsumerServiceUrl()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertNull(setting.getSpSingleLogoutServiceUrl()); @@ -188,8 +190,15 @@ public void testLoadFromFileMinProp() throws IOException, CertificateException, assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -249,8 +258,15 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -336,6 +352,44 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); } + /** + * Tests SettingsBuilder fromFile method + *

+ * Case: all settings config file with multiple Assertion Consumer Services + * + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException + * @throws Error + * + * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile + */ + @Test + public void testLoadFromFileAllPropMultiAssertionConsumerServices() throws IOException, CertificateException, URISyntaxException, SettingsException, Error { + Saml2Settings setting = new SettingsBuilder().fromFile("config/config.all_multi_assertion_consumer_services.properties").build(); + + // let's test only the Attribute Consuming Service part - no need to test again all the rest + final List assertionConsumerService = setting.getSpAssertionConsumerServices(); + assertEquals(2, assertionConsumerService.size()); + + { + final AssertionConsumerService acs1 = assertionConsumerService.get(0); + assertEquals(0, acs1.getIndex()); + assertNull(acs1.isDefault()); + assertEquals("http://localhost:8081/java-saml-jspsample/acs1.jsp", acs1.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", acs1.getBinding()); + } + + { + final AssertionConsumerService acs2 = assertionConsumerService.get(1); + assertEquals(1, acs2.getIndex()); + assertTrue(acs2.isDefault()); + assertEquals("http://localhost:8081/java-saml-jspsample/acs2.jsp", acs2.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs2.getBinding()); + } + } + /** * Tests SettingsBuilder fromFile method * Case: settings config file with certificate string @@ -356,6 +410,13 @@ public void testLoadFromFileCertString() throws IOException, CertificateExceptio assertFalse(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); @@ -411,6 +472,13 @@ public void testLoadFromFileContactString() throws IOException, CertificateExcep assertFalse(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); @@ -522,8 +590,15 @@ public void testLoadFromFileSomeEmptyProp() throws IOException, CertificateExcep assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -576,8 +651,15 @@ public void testLoadFromFileDifferentProp() throws IOException, CertificateExcep assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -665,7 +747,7 @@ public void testFromProperties() throws IOException, Error, CertificateException prop.setProperty(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, setting.getIdpSingleLogoutServiceUrl().toString()); prop.setProperty(SettingsBuilder.IDP_X509CERT_PROPERTY_KEY , x509cert); 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_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, setting.getSpAssertionConsumerServices().get(0).getLocation().toString()); prop.setProperty(SettingsBuilder.SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, setting.getSpSingleLogoutServiceUrl().toString()); Saml2Settings setting2 = new SettingsBuilder().fromProperties(prop).build(); @@ -674,8 +756,15 @@ public void testFromProperties() throws IOException, Error, CertificateException assertTrue(setting2.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting2.getSpEntityId()); + final List assertionConsumerServices = setting2.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting2.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting2.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting2.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting2.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting2.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting2.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -731,8 +820,8 @@ public void testLoadFromValues() throws Exception { // 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"); - samlData.put(SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, "http://localhost:8080/java-saml-jspsample/acs.jsp"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); samlData.put(SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, "http://localhost:8080/java-saml-jspsample/sls.jsp"); samlData.put(SP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); samlData.put(SP_NAMEIDFORMAT_PROPERTY_KEY, "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -810,8 +899,15 @@ public void testLoadFromValues() throws Exception { assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -943,8 +1039,8 @@ public void testLoadFromValuesWithObjects() throws Exception { // 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")); - samlData.put(SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, new URL("http://localhost:8080/java-saml-jspsample/acs.jsp")); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); samlData.put(SP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, new URL("http://localhost:8080/java-saml-jspsample/sls.jsp")); samlData.put(SP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); samlData.put(SP_NAMEIDFORMAT_PROPERTY_KEY, "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -1018,8 +1114,15 @@ public void testLoadFromValuesWithObjects() throws Exception { assertTrue(setting.isStrict()); assertEquals("http://localhost:8080/java-saml-jspsample/metadata.jsp", setting.getSpEntityId()); + final List assertionConsumerServices = setting.getSpAssertionConsumerServices(); + assertEquals(1, assertionConsumerServices.size()); + final AssertionConsumerService acs = assertionConsumerServices.get(0); + assertEquals(1, acs.getIndex()); + assertNull(acs.isDefault()); + assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", acs.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.getBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/acs.jsp", setting.getSpAssertionConsumerServiceUrl().toString()); - assertEquals(setting.getSpAssertionConsumerServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", setting.getSpAssertionConsumerServiceBinding()); assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals(setting.getSpSingleLogoutServiceBinding(), "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); @@ -1112,6 +1215,51 @@ public void testLoadFromValuesWithObjects() throws Exception { assertEquals("ONELOGIN_", setting.getUniqueIDPrefix()); } + /** + * Tests SettingsBuilder constructor + * Case: settings from values when multiple Assertion Consumer Services are defined + * + * @throws IOException + * + * @see com.onelogin.saml2.settings.SettingsBuilder + */ + @Test + public void testLoadFromValuesMultiAssertionConsumerServices() throws Exception { + Map samlData = new LinkedHashMap<>(); + + // just build Assertion Consumer Services + // the following must be ignored, because indexed properties are present + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, "http://localhost:8080/java-saml-jspsample/acs.jsp"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + // the following, instead, must be processed + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, "http://localhost:8081/java-saml-jspsample/acs1.jsp"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ASSERTION_CONSUMER_SERVICE_LOCATION_PROPERTY_KEY_SUFFIX, "http://localhost:8081/java-saml-jspsample/acs2.jsp"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + samlData.put(SP_ASSERTION_CONSUMER_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ASSERTION_CONSUMER_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX, "true"); + + Saml2Settings setting = new SettingsBuilder().fromValues(samlData).build(); + + final List assertionConsumerService = setting.getSpAssertionConsumerServices(); + assertEquals(2, assertionConsumerService.size()); + + { + final AssertionConsumerService acs1 = assertionConsumerService.get(0); + assertEquals(0, acs1.getIndex()); + assertNull(acs1.isDefault()); + assertEquals("http://localhost:8081/java-saml-jspsample/acs1.jsp", acs1.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", acs1.getBinding()); + } + + { + final AssertionConsumerService acs2 = assertionConsumerService.get(1); + assertEquals(1, acs2.getIndex()); + assertTrue(acs2.isDefault()); + assertEquals("http://localhost:8081/java-saml-jspsample/acs2.jsp", acs2.getLocation().toString()); + assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs2.getBinding()); + } + } + /** * Tests SettingsBuilder constructor * Case: settings config file with certificate loaded from file diff --git a/core/src/test/resources/config/config.all_multi_assertion_consumer_services.properties b/core/src/test/resources/config/config.all_multi_assertion_consumer_services.properties new file mode 100644 index 00000000..31000b1d --- /dev/null +++ b/core/src/test/resources/config/config.all_multi_assertion_consumer_services.properties @@ -0,0 +1,215 @@ +# 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 + +# Specify muliple Assertion Consumer Services + +# THE FOLLOWING PROPERTIES FOR SINGLE ASSERTION CONSUMER SERVICE MUST BE IGNORED - MULTIPLE SERVICES DEFINED LATER + +# 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 + +# THE FOLLOWING PROPERTIES MUST BE PROCESSED INSTEAD + +onelogin.saml2.sp.assertion_consumer_service[0].url = http://localhost:8081/java-saml-jspsample/acs1.jsp +onelogin.saml2.sp.assertion_consumer_service[0].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect +onelogin.saml2.sp.assertion_consumer_service[1].url = http://localhost:8081/java-saml-jspsample/acs2.jsp +onelogin.saml2.sp.assertion_consumer_service[1].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST +onelogin.saml2.sp.assertion_consumer_service[1].default = true + +# 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 + +# Attribute Consuming Service name when just one such service should be declared by the SP. +# Comment out or set to empty if no Attribute Consuming Service should be declared, or if multiple ones should (see below). +# The service name is mandatory. +onelogin.saml2.sp.attribute_consuming_service.name = My service + +# Attribute Consuming Service description when just one such service should be declared by the SP. +# Ignored if the previous property is commented or empty. +# The service description is optional. +onelogin.saml2.sp.attribute_consuming_service.description = My service description + +# Language used for Attribute Consuming Service name and description when just one such service should be declared by the SP. +# Ignored if the name property is commented or empty. +# The language is optional and default to "en" (English). +onelogin.saml2.sp.attribute_consuming_service.lang = en + +# Attributes to be included in the Attribute Consuming Service when just one such service should be declared by the SP. +# These are indexed properties, starting from 0. The index is used only to enumerate and sort attributes, but it's required. +# The following properties allow to define each attribute: +# - name: mandatory +# - name_format: optional; if omitted, defaults to urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified +# - friendly_name: optional; if omitted, it won't appear in SP metadata +# - required: optional; if omitted or empty, defaults to false +# - value[x]: an attribute value; the [x] is only used only to enumerate and sort values, but it's required +# Please note that only simple values are currently supported and treated internally as strings. Hence no structured values +# and no ability to specify an xsi:type attribute. +# Attribute values are optional and most often they are simply omitted. +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service.attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service.attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[1].name = FirstName + +# 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 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 = 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 = 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.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 +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 "_". +onelogin.saml2.unique_id_prefix = EXAMPLE diff --git a/core/src/test/resources/config/config.allerrors_multi_assertion_consumer_services.properties b/core/src/test/resources/config/config.allerrors_multi_assertion_consumer_services.properties new file mode 100644 index 00000000..df620b3b --- /dev/null +++ b/core/src/test/resources/config/config.allerrors_multi_assertion_consumer_services.properties @@ -0,0 +1,30 @@ +# we have some ACS with missing information and multiple defaults +onelogin.saml2.sp.assertion_consumer_service[0].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST +onelogin.saml2.sp.assertion_consumer_service[0].default = true +onelogin.saml2.sp.assertion_consumer_service[1].url = http://localhost:8081/java-saml-jspsample/acs2.jsp +onelogin.saml2.sp.assertion_consumer_service[1].default = 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-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- + +# Indicates a requirement for the , and +# elements received by this SP to be signed. +onelogin.saml2.security.want_messages_signed = true + +# 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 + +# Organization +onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.url = http://sp.example.com + +# Contacts +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.min_multi_assertion_consumer_services.properties b/core/src/test/resources/config/config.min_multi_assertion_consumer_services.properties new file mode 100644 index 00000000..f10dc6e5 --- /dev/null +++ b/core/src/test/resources/config/config.min_multi_assertion_consumer_services.properties @@ -0,0 +1,29 @@ +# 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. +onelogin.saml2.sp.assertion_consumer_service[0].url = http://localhost:8081/java-saml-jspsample/acs1.jsp +onelogin.saml2.sp.assertion_consumer_service[1].url = http://localhost:8081/java-saml-jspsample/acs2.jsp +onelogin.saml2.sp.assertion_consumer_service[1].default = true + + +# 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----- \ No newline at end of file diff --git a/core/src/test/resources/config/config.sperrors_multi_assertion_consumer_services.properties b/core/src/test/resources/config/config.sperrors_multi_assertion_consumer_services.properties new file mode 100644 index 00000000..e597c07d --- /dev/null +++ b/core/src/test/resources/config/config.sperrors_multi_assertion_consumer_services.properties @@ -0,0 +1,41 @@ +# 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/ + +# we have some ACS with missing information and multiple defaults +onelogin.saml2.sp.assertion_consumer_service[0].binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST +onelogin.saml2.sp.assertion_consumer_service[0].default = true +onelogin.saml2.sp.assertion_consumer_service[1].url = http://localhost:8081/java-saml-jspsample/acs2.jsp +onelogin.saml2.sp.assertion_consumer_service[1].default = true + +# 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-----\nMIIBrTCCAaGgAwIBAgIBATA + +# Indicates a requirement for the , and +# elements received by this SP to be signed. +onelogin.saml2.security.want_messages_signed = true + +# 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 + +# Organization +onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.displayname = SP Java Example + +# Contacts +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