From 4e2698e2b1cae366e1692101de4aa826def5d003 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 5 Mar 2021 15:46:20 +0100 Subject: [PATCH 1/5] Add support for multiple Attribute Consuming Services Attribute Consuming Services can now be configured in settings. They will then be parsed and added to the generated metadata. On SSO initiation a selector mechanism can be used to select the desired ACS, producing the proper AttributeConsumingServiceIndex attribute in the AuthnRequest. --- README.md | 86 +++++- .../AttributeConsumingServiceSelector.java | 75 ++++++ .../onelogin/saml2/authn/AuthnRequest.java | 8 +- .../saml2/authn/AuthnRequestParams.java | 73 ++++- .../model/AttributeConsumingService.java | 79 +++++- .../com/onelogin/saml2/settings/Metadata.java | 132 +++++---- .../saml2/settings/Saml2Settings.java | 21 ++ .../saml2/settings/SettingsBuilder.java | 101 ++++++- .../saml2/test/authn/AuthnRequestTest.java | 54 ++++ .../saml2/test/settings/MetadataTest.java | 254 ++++++++++++++++-- .../resources/config/config.all.properties | 34 +++ ...ti_attribute_consuming_services.properties | 208 ++++++++++++++ .../config/config.all_specialchars.properties | 34 +++ ...ti_attribute_consuming_services.properties | 62 +++++ 14 files changed, 1150 insertions(+), 71 deletions(-) create mode 100644 core/src/main/java/com/onelogin/saml2/authn/AttributeConsumingServiceSelector.java create mode 100644 core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties create mode 100644 core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties diff --git a/README.md b/README.md index df0c3bf0..ccbf12b1 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,9 @@ 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 +# Attribute Consuming Services +# SEE BELOW + ## Identity Provider Data that we want connect with our SP ## # Identifier of the IdP entity (must be a URI) @@ -510,8 +513,87 @@ The getSPMetadata will return the metadata signed or not based on the security p Before the XML metadata is exposed, a check takes place to ensure that the info to be provided is valid. -##### Attribute Consumer Service(ACS) -This code handles the SAML response that the IdP forwards to the SP through the user's client. +##### Attribute Consuming Service (ACS) +The SP may optionally specify one or more Attribute Consuming Services in its metadata. These can be configured in the settings. + +If just one ACS is required: + +```properties +# Attribute Consuming Service name when just one ACS should be declared by the SP. +# Comment out or set to empty if no ACS 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 ACS 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 ACS 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 + +# Requested attributes to be included in the Attribute Consuming Service when just one ACS should be declared by the SP. +# At least one requested attribute must be specified, otherwise schema validation will fail. +# Attribute properties 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 requested 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 +``` + +If multiple ACSs are required, they can be specified in a similar way, but using indexes: these indexes are used to enumerate and +identify attribute consuming services within the SP metadata and can be subsequently used in the auth process to specify which +attribute set should be requested to the IdP. The "default" property can also be set to designate the default ACS. Here is an example: + +```properties +onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service[1].name = Anagrafica +onelogin.saml2.sp.attribute_consuming_service[1].description = Set completo +onelogin.saml2.sp.attribute_consuming_service[1].lang = it +onelogin.saml2.sp.attribute_consuming_service[1].default = true +onelogin.saml2.sp.attribute_consuming_service[1].attribute[0].name = FirstName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].name = LastName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].required = true +``` + +Please note that if you specify (multiple) indexed Attribute Consuming Services, the non-indexed properties will be ignored. + +As said, to request a specific attribute set when initiating SSO, a selection mechanism is available: + +```java +import static com.onelogin.saml2.authn.AttributeConsumingServiceSelector.*; +Auth auth = new Auth(request, response); +// select by index 1 +auth.login(new AuthnRequestParams(false, false, true, byIndex(1)); +// or select by ACS name +auth.login(new AuthnRequestParams(false, false, true, byServiceName(auth.getSettings(), "Anagrafica")); +// or see AttributeConsumingServiceSelector interface implementations for more options +``` + +If no selector is specified, `AttributeConsumingServiceSelector.useDefault()` will be used, which will simply omit any +`AttributeConsumingServiceIndex` from the request, hence leaving the IdP choose the default attribute set agreed upon. + +Then, the following code handles the SAML response that the IdP forwards to the SP through the user's client: + ```java Auth auth = new Auth(request, response); auth.processResponse(); diff --git a/core/src/main/java/com/onelogin/saml2/authn/AttributeConsumingServiceSelector.java b/core/src/main/java/com/onelogin/saml2/authn/AttributeConsumingServiceSelector.java new file mode 100644 index 00000000..1a478ed3 --- /dev/null +++ b/core/src/main/java/com/onelogin/saml2/authn/AttributeConsumingServiceSelector.java @@ -0,0 +1,75 @@ +package com.onelogin.saml2.authn; + +import java.util.List; + +import com.onelogin.saml2.model.AttributeConsumingService; +import com.onelogin.saml2.settings.Saml2Settings; + +/** + * Interfaced used to select the Attribute Consuming Service 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 AttributeConsumingServiceSelector { + + /** + * @return a selector of the default Attribute Consuming Service + */ + static AttributeConsumingServiceSelector useDefault() { + return () -> null; + } + + /** + * @param attributeConsumingService + * the Attribute Consuming Service to select + * @return a selector the chooses the specified Attribute Consuming Service; + * indeed, its index is used + */ + static AttributeConsumingServiceSelector use(final AttributeConsumingService attributeConsumingService) { + return byIndex(attributeConsumingService.getIndex()); + } + + /** + * @param index + * the index of the Attribute Consuming Service to select + * @return a selector that chooses the Attribute Consuming Service with the + * given index + */ + static AttributeConsumingServiceSelector byIndex(final int index) { + return () -> index; + } + + /** + * @param settings + * the SAML settings, containing the list of the available + * Attribute Consuming Services (see + * {@link Saml2Settings#getSpAttributeConsumingServices()}) + * @param serviceName + * the name of the Attribute Consuming Service to select + * @return a selector that chooses the Attribute Consuming Service with the + * given name; please note that this selector will select the default + * service if no one is found with the given name + */ + static AttributeConsumingServiceSelector byServiceName(final Saml2Settings settings, final String serviceName) { + return () -> { + final List services = settings.getSpAttributeConsumingServices(); + if (services != null) + return services.stream().filter(service -> service.getServiceName().equals(serviceName)) + .findFirst().map(AttributeConsumingService::getIndex).orElse(null); + else + return null; + }; + } + + /** + * Returns the index of the selected Attribute Consuming Service. + * + * @return the service index, or null if the default one should be selected + */ + Integer getAttributeConsumingServiceIndex(); +} \ 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..594e62ad 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequest.java @@ -266,6 +266,12 @@ private StrSubstitutor generateSubstitutor(AuthnRequestParams params, Saml2Setti } valueMap.put("requestedAuthnContextStr", requestedAuthnContextStr); + + String attributeConsumingServiceIndexStr = ""; + final Integer acsIndex = params.getAttributeConsumingServiceSelector().getAttributeConsumingServiceIndex(); + if (acsIndex != null) + attributeConsumingServiceIndexStr = " AttributeConsumingServiceIndex=\"" + acsIndex + "\""; + valueMap.put("attributeConsumingServiceIndexStr", attributeConsumingServiceIndexStr); return new StrSubstitutor(valueMap); } @@ -275,7 +281,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..f361942c 100644 --- a/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java +++ b/core/src/main/java/com/onelogin/saml2/authn/AuthnRequestParams.java @@ -27,8 +27,15 @@ public class AuthnRequestParams { */ private final String nameIdValueReq; + /* + * / Selector to use to specify the Attribute Consuming Service index + */ + private AttributeConsumingServiceSelector attributeConsumingServiceSelector; + /** - * Create a set of authentication request input parameters. + * Create a set of authentication request input parameters. The + * {@link AttributeConsumingServiceSelector#useDefault()} selector is used to + * select the Attribute Consuming Service. * * @param forceAuthn * whether the ForceAuthn attribute should be set to @@ -71,6 +78,29 @@ 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 + * true + * @param setNameIdPolicy + * whether a NameIDPolicy should be set + * @param attributeConsumingServiceSelector + * the selector to use to specify the Attribute Consuming Service + * index; if null, + * {@link AttributeConsumingServiceSelector#useDefault()} is used + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, + AttributeConsumingServiceSelector attributeConsumingServiceSelector) { + this(forceAuthn, isPassive, setNameIdPolicy, true, null, attributeConsumingServiceSelector); + } + + /** + * Create a set of authentication request input parameters. The + * {@link AttributeConsumingServiceSelector#useDefault()} selector is used to + * select the Attribute Consuming Service. + * + * @param forceAuthn + * whether the ForceAuthn attribute should be set to + * true + * @param isPassive * whether the IsPassive attribute should be set to * true * @param setNameIdPolicy @@ -103,13 +133,44 @@ 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 attributeConsumingServiceSelector + * the selector to use to specify the Attribute Consuming Service + * index; if null, + * {@link AttributeConsumingServiceSelector#useDefault()} is used + */ + public AuthnRequestParams(boolean forceAuthn, boolean isPassive, boolean setNameIdPolicy, boolean allowCreate, + String nameIdValueReq, AttributeConsumingServiceSelector attributeConsumingServiceSelector) { this.forceAuthn = forceAuthn; this.isPassive = isPassive; this.setNameIdPolicy = setNameIdPolicy; this.allowCreate = allowCreate; this.nameIdValueReq = nameIdValueReq; + this.attributeConsumingServiceSelector = attributeConsumingServiceSelector != null + ? attributeConsumingServiceSelector + : AttributeConsumingServiceSelector.useDefault(); } - + /** * Create a set of authentication request input parameters, by copying them from * another set. @@ -123,6 +184,7 @@ protected AuthnRequestParams(AuthnRequestParams source) { this.setNameIdPolicy = source.isSetNameIdPolicy(); this.allowCreate = source.isAllowCreate(); this.nameIdValueReq = source.getNameIdValueReq(); + this.attributeConsumingServiceSelector = source.getAttributeConsumingServiceSelector(); } /** @@ -163,4 +225,11 @@ public boolean isAllowCreate() { public String getNameIdValueReq() { return nameIdValueReq; } + + /** + * @return the selector to use to specify the Attribute Consuming Service index + */ + public AttributeConsumingServiceSelector getAttributeConsumingServiceSelector() { + return attributeConsumingServiceSelector; + } } \ No newline at end of file diff --git a/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java b/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java index 2086686d..3429bf93 100644 --- a/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java +++ b/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java @@ -11,6 +11,14 @@ */ public class AttributeConsumingService { /** + * Service Index + */ + private final int index; + /** + * Whether this service is the default one + */ + private final Boolean isDefault; + /** * Service Name */ private final String serviceName; @@ -20,6 +28,11 @@ public class AttributeConsumingService { */ private final String serviceDescription; + /** + * Language used for service name and description + */ + private final String lang; + /** * Requested Attributes */ @@ -27,18 +40,59 @@ public class AttributeConsumingService { /** * Constructor - * + * + * @param index + * int. Service index + * @param isDefault + * boolean. Whether it's the default attribute consuming service * @param serviceName * String. Service Name * @param serviceDescription * String. Service Description + * @param lang + * String. Language in which service name and description are + * written; defaults to en if null is specified */ - public AttributeConsumingService(String serviceName, String serviceDescription) { + public AttributeConsumingService(int index, Boolean isDefault, String serviceName, String serviceDescription, String lang) { + this.index = index; + this.isDefault = isDefault; this.serviceName = serviceName != null? serviceName : ""; this.serviceDescription = serviceDescription != null? serviceDescription : ""; + this.lang = lang != null? lang: "en"; this.requestedAttributes = new ArrayList(); } + /** + * Constructor. Service name and description are assumed to be in English. + * + * @param index + * int. Service index + * @param isDefault + * boolean. Whether it's the default attribute consuming service + * @param serviceName + * String. Service Name + * @param serviceDescription + * String. Service Description + */ + public AttributeConsumingService(int index, Boolean isDefault, String serviceName, String serviceDescription) { + this(index, isDefault, serviceName, serviceDescription, null); + } + + /** + * Constructor for a non-default attribute consuming service with index 1 + * and service name and descriptions in English. + * Mainly kept for backward compatibility, this constructor can be used when an only + * attribute consuming service is required. + * + * @param serviceName + * String. Service Name + * @param serviceDescription + * String. Service Description + */ + public AttributeConsumingService(String serviceName, String serviceDescription) { + this(1, null, serviceName, serviceDescription, null); + } + /** * @param attr * RequestedAttribute. The requested attribute to be included @@ -47,6 +101,20 @@ public final void addRequestedAttribute(RequestedAttribute attr) { this.requestedAttributes.add(attr); } + /** + * @return int the service index + */ + public final int getIndex() { + return index; + } + + /** + * @return boolean whether this is the default attribute consuming service + */ + public final Boolean isDefault() { + return isDefault; + } + /** * @return string the service name */ @@ -61,6 +129,13 @@ public final String getServiceDescription() { return serviceDescription; } + /** + * @return string the language in which service name and description are written + */ + public String getLang() { + return lang; + } + /** * @return List the requested attributes */ 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..6834478a 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -43,8 +43,11 @@ public class Metadata { private static final int SECONDS_CACHED = 604800; // 1 week /** - * AttributeConsumingService - */ + * AttributeConsumingService + * + * @deprecated Attribute Consuming Services should be specified in settings + */ + @Deprecated private AttributeConsumingService attributeConsumingService = null; /** @@ -70,7 +73,12 @@ public class Metadata { * @param cacheDuration Duration of the cache in seconds * @param attributeConsumingService AttributeConsumingService of service provider * @throws CertificateEncodingException + * @deprecated Attribute Consuming Services should be specified in settings; if + * a non-null service is specified here, it will be + * used in place of the ones specified in settings to generate + * metadata */ + @Deprecated public Metadata(Saml2Settings settings, Calendar validUntilTime, Integer cacheDuration, AttributeConsumingService attributeConsumingService) throws CertificateEncodingException { this.validUntilTime = validUntilTime; this.attributeConsumingService = attributeConsumingService; @@ -169,7 +177,12 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif valueMap.put("spAssertionConsumerServiceUrl", Util.toXml(settings.getSpAssertionConsumerServiceUrl().toString())); valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); - valueMap.put("strAttributeConsumingService", getAttributeConsumingServiceXml()); + // if an ACS was specified at construction time, use it in place of the ones specified in settings + // this is for backward compatibility + valueMap.put("strAttributeConsumingService", + toAttributeConsumingServicesXml(attributeConsumingService != null + ? Arrays.asList(attributeConsumingService) + : settings.getSpAttributeConsumingServices())); valueMap.put("strKeyDescriptor", toX509KeyDescriptorsXML(settings.getSPcert(), settings.getSPcertNew(), wantsEncrypted)); @@ -206,63 +219,86 @@ private static StringBuilder getMetadataTemplate() { /** * Generates the AttributeConsumingService section of the metadata's template + * + * @param attributeConsumingServices + * a list containing the Attribute Consuming Services to generate + * the metadata for * * @return the AttributeConsumingService section of the metadata's template */ - private String getAttributeConsumingServiceXml() { + private String toAttributeConsumingServicesXml(List attributeConsumingServices) { + final StringBuilder acssXml = new StringBuilder(); + if (attributeConsumingServices != null) + attributeConsumingServices.stream().forEach(service -> acssXml.append(toAttributeConsumingServiceXml(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 toAttributeConsumingServiceXml(AttributeConsumingService service) { + int index = service.getIndex(); + Boolean isDefault = service.isDefault(); + String serviceName = service.getServiceName(); + String serviceDescription = service.getServiceDescription(); + String lang = service.getLang(); + List requestedAttributes = service.getRequestedAttributes(); StringBuilder attributeConsumingServiceXML = new StringBuilder(); - if (attributeConsumingService != null) { - String serviceName = attributeConsumingService.getServiceName(); - String serviceDescription = attributeConsumingService.getServiceDescription(); - List requestedAttributes = attributeConsumingService.getRequestedAttributes(); - - attributeConsumingServiceXML.append(""); - if (serviceName != null && !serviceName.isEmpty()) { - attributeConsumingServiceXML.append("" + Util.toXml(serviceName) + ""); - } - if (serviceDescription != null && !serviceDescription.isEmpty()) { - attributeConsumingServiceXML.append("" + Util.toXml(serviceDescription) + ""); - } - if (requestedAttributes != null && !requestedAttributes.isEmpty()) { - for (RequestedAttribute requestedAttribute : requestedAttributes) { - String name = requestedAttribute.getName(); - String friendlyName = requestedAttribute.getFriendlyName(); - String nameFormat = requestedAttribute.getNameFormat(); - Boolean isRequired = requestedAttribute.isRequired(); - List attrValues = requestedAttribute.getAttributeValues(); - - String contentStr = ""); + if (serviceName != null && !serviceName.isEmpty()) { + attributeConsumingServiceXML.append("") + .append(Util.toXml(serviceName)).append(""); + } + if (serviceDescription != null && !serviceDescription.isEmpty()) { + attributeConsumingServiceXML.append("") + .append(Util.toXml(serviceDescription)).append(""); + } + if (requestedAttributes != null && !requestedAttributes.isEmpty()) { + for (RequestedAttribute requestedAttribute : requestedAttributes) { + String name = requestedAttribute.getName(); + String friendlyName = requestedAttribute.getFriendlyName(); + String nameFormat = requestedAttribute.getNameFormat(); + Boolean isRequired = requestedAttribute.isRequired(); + List attrValues = requestedAttribute.getAttributeValues(); + + StringBuilder contentStr = new StringBuilder("" + Util.toXml(attrValue) + ""; - } - attributeConsumingServiceXML.append(contentStr + ""); - } else { - attributeConsumingServiceXML.append(contentStr + " />"); + if (attrValues != null && !attrValues.isEmpty()) { + contentStr.append(">"); + for (String attrValue : attrValues) { + contentStr.append("").append(Util.toXml(attrValue)).append(""); } + attributeConsumingServiceXML.append(contentStr).append(""); + } else { + attributeConsumingServiceXML.append(contentStr).append(" />"); } } - attributeConsumingServiceXML.append(""); } - + attributeConsumingServiceXML.append(""); return attributeConsumingServiceXML.toString(); } diff --git a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java index 9ee29465..103f271c 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -17,6 +17,8 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; + +import com.onelogin.saml2.model.AttributeConsumingService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.Organization; import com.onelogin.saml2.util.Constants; @@ -49,6 +51,7 @@ public class Saml2Settings { private X509Certificate spX509certNew = null; private PrivateKey spPrivateKey = null; private HSM hsm = null; + private List spAttributeConsumingServices = new ArrayList<>(); // IdP private String idpEntityId = ""; @@ -125,6 +128,13 @@ public final String getSpAssertionConsumerServiceBinding() { return spAssertionConsumerServiceBinding; } + /** + * @return the SP Attribute Consuming Services + */ + public final List getSpAttributeConsumingServices() { + return spAttributeConsumingServices; + } + /** * @return the spSingleLogoutServiceUrl setting value */ @@ -905,6 +915,17 @@ public void setTrimAttributeValues(boolean trimAttributeValues) { public boolean isTrimAttributeValues() { return trimAttributeValues; } + + /** + * Set the Attribute Consuming Services to be declared in the Service Provider + * metadata + * + * @param spAttributeConsumingServices + * the Attribute Consuming Services to set + */ + protected final void setSpAttributeConsumingServices(List spAttributeConsumingServices) { + this.spAttributeConsumingServices = spAttributeConsumingServices; + } /** * Set contacts info that will be listed on the Service Provider metadata diff --git a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 6232044e..77217e9d 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -31,10 +31,13 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import com.onelogin.saml2.exception.Error; +import com.onelogin.saml2.model.AttributeConsumingService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.model.Organization; +import com.onelogin.saml2.model.RequestedAttribute; import com.onelogin.saml2.util.Constants; import com.onelogin.saml2.util.Util; @@ -82,6 +85,19 @@ public class SettingsBuilder { public final static String SP_CONTACT_EMAIL_ADDRESS_PROPERTY_KEY_PREFIX = "email_address"; public final static String SP_CONTACT_TELEPHONE_NUMBER_PROPERTY_KEY_PREFIX = "telephone_number"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX = "onelogin.saml2.sp.attribute_consuming_service"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX = "name"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX = "description"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX = "lang"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX = "default"; + + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX = "attribute"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX = "name"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX = "name_format"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX = "friendly_name"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX = "required"; + public final static String SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX = "value"; + // KeyStore public final static String KEYSTORE_KEY = "onelogin.saml2.keystore.store"; public final static String KEYSTORE_ALIAS = "onelogin.saml2.keystore.alias"; @@ -518,6 +534,75 @@ private List loadContacts() { return contacts; } + + /** + * Loads the Attribute Consuming Services from settings. + * + * @return a list containing the loaded Attribute Consuming Services + */ + private List loadAttributeConsumingServices() { + // first split properties into a map of properties + // key = ACS index; value = ACS properties + final SortedMap> acsProps = + extractIndexedProperties(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX, samlData); + // then build each ACS + if(acsProps.containsKey(-1) && acsProps.size() == 1) + // single ACS specified; use index 1 for backward compatibility + return Arrays.asList(loadAttributeConsumingService(acsProps.get(-1), 1)); + else + // multiple indexed ACSs specified + return acsProps.entrySet().stream() + // ignore non-indexed ACS + .filter(entry -> entry.getKey() != -1) + .map(entry -> loadAttributeConsumingService(entry.getValue(), entry.getKey())) + .collect(Collectors.toList()); + } + + /** + * Loads a single Attribute Consuming Service from settings. + * + * @param acsProps + * a map containing the ACS settings + * @param index + * the index to be set on the returned ACS + * @return the loaded ACS + */ + private AttributeConsumingService loadAttributeConsumingService(Map acsProps, int index) { + final String serviceName = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, acsProps); + final String serviceDescription = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX, acsProps); + final String lang = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX, acsProps); + final Boolean isDefault = loadBooleanProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX, acsProps); + final AttributeConsumingService acs = new AttributeConsumingService(index, isDefault, serviceName, serviceDescription, lang); + // split properties into a map of properties + // key = attribute index; value = attribute properties + final SortedMap> attributeProps = extractIndexedProperties(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX, acsProps); + // build attributes + attributeProps.forEach((attributeIndex, attributeData) -> { + acs.addRequestedAttribute(loadRequestedAttribute(attributeData)); + }); + return acs; + } + + /** + * Loads a requested attribute from settings. + * + * @param attributeProps + * a map containing the attribute settings + * @return the loaded attribute + */ + private RequestedAttribute loadRequestedAttribute(Map attributeProps) { + final String name = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, attributeProps); + final String nameFormat = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX, attributeProps); + final String friendlyName = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX, attributeProps); + final Boolean required = loadBooleanProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, attributeProps); + // split properties into a map of properties + // key = value index; value = the actual value + final SortedMap values = extractIndexedValues(SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX, attributeProps); + final List stringValues = values.values().stream() + .map(value -> isString(value) ? StringUtils.trimToNull((String) value) : null) + .filter(Objects::nonNull).collect(Collectors.toList()); + return new RequestedAttribute(name, friendlyName, required, nameFormat, stringValues); + } /** * Loads a single contact from settings. @@ -732,6 +817,8 @@ private void loadSpSetting() { if (spNameIDFormat != null && !spNameIDFormat.isEmpty()) { saml2Setting.setSpNameIDFormat(spNameIDFormat); } + + saml2Setting.setSpAttributeConsumingServices(loadAttributeConsumingServices()); boolean keyStoreEnabled = this.samlData.get(KEYSTORE_KEY) != null && this.samlData.get(KEYSTORE_ALIAS) != null && this.samlData.get(KEYSTORE_KEY_PASSWORD) != null; @@ -799,7 +886,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()); } 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..81582563 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 @@ -16,8 +16,10 @@ import org.junit.Assert; import org.junit.Test; +import com.onelogin.saml2.authn.AttributeConsumingServiceSelector; import com.onelogin.saml2.authn.AuthnRequest; import com.onelogin.saml2.authn.AuthnRequestParams; +import com.onelogin.saml2.model.AttributeConsumingService; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Util; @@ -482,6 +484,58 @@ public void testSubjectSpecialChars() throws Exception { assertThat(authnRequestStr, containsString("")); } + /** + * Tests the AuthnRequest Constructor + * The creation of a deflated SAML Request with the index of the desired Attribute Consuming Service + * + * @throws Exception + * + * @see com.onelogin.saml2.authn.AuthnRequest + */ + @Test + public void testAttributeConsumingServiceSelector() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_multi_attribute_consuming_services.properties").build(); + + AuthnRequest authnRequest = new AuthnRequest(settings); + String authnRequestStringBase64 = authnRequest.getEncodedAuthnRequest(); + String authnRequestStr = Util.base64decodedInflated(authnRequestStringBase64); + assertThat(authnRequestStr, containsString(""; + String sNameStr = "Test Service"; + String sDescStr = "Test Service Desc"; + String reqAttr1Str = ""; + String reqAttr2Str = ""; + String reqAttr3Str = ""; + String footerStr = ""; + + assertThat(metadataStr, containsString(headerStr)); + assertThat(metadataStr, containsString(sNameStr)); + assertThat(metadataStr, containsString(sDescStr)); + assertThat(metadataStr, containsString(reqAttr1Str)); + assertThat(metadataStr, containsString(reqAttr2Str)); + assertThat(metadataStr, containsString(reqAttr3Str)); + assertThat(metadataStr, containsString(footerStr)); + } + + /** + * Tests the toAttributeConsumingServicesXml method of Metadata when an Attribute Consuming Service + * is specified at construction time + *

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

+ * Case: Single non-default AttributeConsumingService with no explicit index. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml + */ + @Test + public void testToAttributeConsumingServiceXmlNoIndexLegacy() throws IOException, CertificateEncodingException, Error { Saml2Settings settings = getSettingFromAllProperties(); AttributeConsumingService attributeConsumingService = new AttributeConsumingService("Test Service", "Test Service Desc"); @@ -492,17 +577,18 @@ public void testGetAttributeConsumingServiceXml() throws IOException, Certificat } /** - * Tests the getAttributeConsumingServiceXml method of Metadata + * Tests the toAttributeConsumingServicesXml method of Metadata when an Attribute Consuming Service + * is specified at construction time *

- * Case: Attribute Consuming Service definition contains special chars. + * Case: Single non-default AttributeConsumingService with no explicit index and containing special chars. * * @throws IOException * @throws CertificateEncodingException * @throws Error - * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml */ @Test - public void testGetAttributeConsumingServiceXmlSpecialChars() throws IOException, CertificateEncodingException, Error { + public void testToAttributeConsumingServiceXmlNoIndexLegacySpecialChars() throws IOException, CertificateEncodingException, Error { Saml2Settings settings = getSettingFromAllProperties(); AttributeConsumingService attributeConsumingService = new AttributeConsumingService("T&st Service", "T&st Service Desc"); @@ -531,20 +617,21 @@ public void testGetAttributeConsumingServiceXmlSpecialChars() throws IOException } /** - * Tests the getAttributeConsumingServiceXml method of Metadata + * Tests the toAttributeConsumingServicesXml method of Metadata when an Attribute Consuming Service + * is specified at construction time *

* Case: AttributeConsumingService Multiple AttributeValue. * * @throws IOException * @throws CertificateEncodingException * @throws Error - * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml */ @Test - public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() throws IOException, CertificateEncodingException, Error { + public void testToAttributeConsumingServiceXmlWithMultipleAttributeValueLegacy() throws IOException, CertificateEncodingException, Error { Saml2Settings settings = getSettingFromAllProperties(); - AttributeConsumingService attributeConsumingService = new AttributeConsumingService("Test Service", "Test Service Desc"); + AttributeConsumingService attributeConsumingService = new AttributeConsumingService(0, true, "Test Service", "Test Service Desc", "en"); List attrValues = new ArrayList(); attrValues.add("userType"); attrValues.add("admin"); @@ -557,7 +644,7 @@ public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() thro Metadata metadataObj = new Metadata(settings, null, null, attributeConsumingService); String metadataStr = metadataObj.getMetadataString(); - String headerStr = ""; + String headerStr = ""; String sNameStr = "Test Service"; String sDescStr = "Test Service Desc"; String reqAttr1Str = ""; @@ -577,17 +664,18 @@ public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValue() thro } /** - * Tests the getAttributeConsumingServiceXml method of Metadata + * Tests the toAttributeConsumingServicesXml method of Metadata when an Attribute Consuming Service + * is specified at construction time *

* Case: AttributeConsumingService Multiple AttributeValue with special chars. * * @throws IOException * @throws CertificateEncodingException * @throws Error - * @see com.onelogin.saml2.settings.Metadata#getAttributeConsumingServiceXml + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml */ @Test - public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValueSpecialChars() throws IOException, CertificateEncodingException, Error { + public void testToAttributeConsumingServiceXmlWithMultipleAttributeValueLegacySpecialChars() throws IOException, CertificateEncodingException, Error { Saml2Settings settings = getSettingFromAllProperties(); AttributeConsumingService attributeConsumingService = new AttributeConsumingService("T&st Service", "T&st Service Desc"); @@ -622,6 +710,138 @@ public void testGetAttributeConsumingServiceXmlWithMultipleAttributeValueSpecial assertThat(metadataStr, containsString(footerStr)); } + /** + * Tests the toAttributeConsumingServicesXml method of Metadata + *

+ * Case: single Attribute Consuming Service specified in settings. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml + */ + @Test + public void testToAttributeConsumingServiceXmlSingleACS() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllProperties(); + + Metadata metadataObj = new Metadata(settings, null, null); + String metadataStr = metadataObj.getMetadataString(); + + String headerStr = ""; + String sNameStr = "My service"; + String sDescStr = "My service description"; + String reqAttr1Str = ""; + String reqAttr1Atr1Str = "foo@example.org"; + String reqAttr1Attr2Str = "bar@example.org"; + String reqAttr2Str = ""; + String footerStr = ""; + + assertThat(metadataStr, containsString(headerStr)); + assertThat(metadataStr, containsString(sNameStr)); + assertThat(metadataStr, containsString(sDescStr)); + assertThat(metadataStr, containsString(reqAttr1Str)); + assertThat(metadataStr, containsString(reqAttr1Atr1Str)); + assertThat(metadataStr, containsString(reqAttr1Attr2Str)); + assertThat(metadataStr, containsString(reqAttr2Str)); + assertThat(metadataStr, containsString(footerStr)); + } + + /** + * Tests the toAttributeConsumingServicesXml method of Metadata + *

+ * Case: single Attribute Consuming Service specified in settings. + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml + */ + @Test + public void testToAttributeConsumingServiceXmlSingleACSSpecialChars() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllSpecialCharsProperties(); + + Metadata metadataObj = new Metadata(settings, null, null); + String metadataStr = metadataObj.getMetadataString(); + + String headerStr = ""; + String sNameStr = "My s&rvice"; + String sDescStr = "My s&rvice description"; + String reqAttr1Str = ""; + String reqAttr1Atr1Str = "foo&bar@example.org"; + String reqAttr1Attr2Str = "bar@example.org"; + String reqAttr2Str = ""; + String footerStr = ""; + + assertThat(metadataStr, containsString(headerStr)); + assertThat(metadataStr, containsString(sNameStr)); + assertThat(metadataStr, containsString(sDescStr)); + assertThat(metadataStr, containsString(reqAttr1Str)); + assertThat(metadataStr, containsString(reqAttr1Atr1Str)); + assertThat(metadataStr, containsString(reqAttr1Attr2Str)); + assertThat(metadataStr, containsString(reqAttr2Str)); + assertThat(metadataStr, containsString(footerStr)); + } + + /** + * Tests the toAttributeConsumingServicesXml method of Metadata + * Case: single Attribute Consuming Service specified in settings + * + * @throws IOException + * @throws CertificateEncodingException + * @throws Error + * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml + */ + @Test + public void testToAttributeConsumingServiceXmlMultiACS() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllPropertiesMultiACS(); + + Metadata metadataObj = new Metadata(settings, null, null); + String metadataStr = metadataObj.getMetadataString(); + + String header1Str = ""; + String sName1Str = "Just e-mail"; + String reqAttr11Str = ""; + String reqAttr11Atr1Str = "foo@example.org"; + String reqAttr11Attr2Str = "bar@example.org"; + String footer1Str = ""; + + String header2Str = ""; + String sName2Str = "Anagrafica "completa""; + String sDesc2Str = "Dati anagrafici "completi""; + String reqAttr21Str = ""; + String reqAttr22Str = ""; + String footer2Str = ""; + + assertThat(metadataStr, containsString(header1Str)); + assertThat(metadataStr, containsString(sName1Str)); + assertThat(metadataStr, containsString(reqAttr11Str)); + assertThat(metadataStr, containsString(reqAttr11Atr1Str)); + assertThat(metadataStr, containsString(reqAttr11Attr2Str)); + assertThat(metadataStr, containsString(footer1Str)); + + assertThat(metadataStr, containsString(header2Str)); + assertThat(metadataStr, containsString(sName2Str)); + assertThat(metadataStr, containsString(sDesc2Str)); + assertThat(metadataStr, containsString(reqAttr21Str)); + assertThat(metadataStr, containsString(reqAttr22Str)); + assertThat(metadataStr, containsString(footer2Str)); + + // properties for the single non-indexed ACS must NOT be present in this case + String sNameStr = "My service"; + String sDescStr = "My service description"; + String reqAttr1Str = ""; + String reqAttr1Atr1Str = "foo_wrong@example.org"; + String reqAttr1Attr2Str = "bar_wrong@example.org"; + String reqAttr2Str = ""; + + assertThat(metadataStr, not(containsString(sNameStr))); + assertThat(metadataStr, not(containsString(sDescStr))); + assertThat(metadataStr, not(containsString(reqAttr1Str))); + assertThat(metadataStr, not(containsString(reqAttr1Atr1Str))); + assertThat(metadataStr, not(containsString(reqAttr1Attr2Str))); + assertThat(metadataStr, not(containsString(reqAttr2Str))); + } + /** * Tests the signMetadata method of Metadata * Case imported metadata @@ -707,6 +927,10 @@ private Saml2Settings getSettingFromAllSpecialCharsProperties() throws Error, IO return new SettingsBuilder().fromFile("config/config.all_specialchars.properties").build(); } + private Saml2Settings getSettingFromAllPropertiesMultiACS() throws Error, IOException { + return new SettingsBuilder().fromFile("config/config.all_multi_attribute_consuming_services.properties").build(); + } + @Test public void shouldIncludeValidUntilAndDuration() throws CertificateEncodingException, Error, IOException { //given diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index fcdbf242..ce8a7a83 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -31,6 +31,40 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified +# Attribute Consuming Service name when just one ACS should be declared by the SP. +# Comment out or set to empty if no ACS 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 ACS 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 ACS 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 ACS 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----- diff --git a/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties new file mode 100644 index 00000000..160dfda9 --- /dev/null +++ b/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties @@ -0,0 +1,208 @@ +# If 'strict' is True, then the Java Toolkit will reject unsigned +# or unencrypted messages if it expects them signed or encrypted +# Also will reject the messages if not strictly follow the SAML +onelogin.saml2.strict = true + +# Enable debug mode (to print errors) +onelogin.saml2.debug = true + +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp +# SAML protocol binding to be used when returning the or sending the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-POST binding only +onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp + +# SAML protocol binding for the Single Logout Service of the SP. +# Onelogin Toolkit supports for this endpoint the HTTP-Redirect binding only +onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Specifies constraints on the name identifier to be used to +# represent the requested subject. +# Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported +onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + +# THE FOLLOWING PROPERTIES FOR SINGLE ACS MUST BE IGNORED - MULTIPLE SERVICES DEFINED LATER + +# Attribute Consuming Service name when just one ACS should be declared by the SP. +# Comment out or set to empty if no ACS 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 ACS 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 ACS 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 ACS 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_Wrong +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_wrong@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[1] = bar_wrong@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[1].name = FirstName_Wrong + +# THE FOLLOWING PROPERTIES MUST BE PROCESSED INSTEAD + +onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service[1].name = Anagrafica "completa" +onelogin.saml2.sp.attribute_consuming_service[1].description = Dati anagrafici "completi" +onelogin.saml2.sp.attribute_consuming_service[1].lang = it +onelogin.saml2.sp.attribute_consuming_service[1].default = true +onelogin.saml2.sp.attribute_consuming_service[1].attribute[0].name = Birth date +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].name = First & Last Name +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].required = 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----- + +# To be used during SP Key roll over +onelogin.saml2.sp.x509certNew = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- + +# Requires Format PKCS#8 BEGIN PRIVATE KEY +# If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem +onelogin.saml2.sp.privatekey = -----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY----- + +# Identity Provider Data that we want connect with our SP +# Identifier of the IdP entity (must be a URI) +onelogin.saml2.idp.entityid = http://idp.example.com/ + +# SSO endpoint info of the IdP. (Authentication Request protocol) +# URL Target of the IdP where the SP will send the Authentication Request Message +onelogin.saml2.idp.single_sign_on_service.url = http://idp.example.com/simplesaml/saml2/idp/SSOService.php + +# SAML protocol binding to be used to deliver the message +# to the IdP. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# SLO endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Request +onelogin.saml2.idp.single_logout_service.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutService.php + +# Optional SLO Response endpoint info of the IdP. +# URL Location of the IdP where the SP will send the SLO Response. If left blank, same URL as onelogin.saml2.idp.single_logout_service.url will be used. +# Some IdPs use a separate URL for sending a logout request and response, use this property to set the separate response url +onelogin.saml2.idp.single_logout_service.response.url = http://idp.example.com/simplesaml/saml2/idp/SingleLogoutServiceResponse.php + +# SAML protocol binding to be used when returning the +# message. Onelogin Toolkit supports for this endpoint the +# HTTP-Redirect binding only +onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect + +# Public x509 certificate of the IdP +onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloXDTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAXBgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDugMK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0edMOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMBAA==\n-----END CERTIFICATE----- +onelogin.saml2.idp.certfingerprint = 4b6f70bb2cab82c86a8270f71a880b62e25bc2b3 +onelogin.saml2.idp.certfingerprint_algorithm = sha1 + +# Security settings +# + +# Indicates that the nameID of the sent by this SP +# will be encrypted. +onelogin.saml2.security.nameid_encrypted = true + +# Indicates whether the messages sent by this SP +# will be signed. [The Metadata of the SP will offer this info] +onelogin.saml2.security.authnrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutrequest_signed = true + +# Indicates whether the messages sent by this SP +# will be signed. +onelogin.saml2.security.logoutresponse_signed = true + +# Indicates a requirement for the , and +# elements received by this SP to be signed. +onelogin.saml2.security.want_messages_signed = true + +# Indicates a requirement for the of the to be signed +onelogin.saml2.security.want_assertions_signed = true + +# Indicates a requirement for the Metadata of this SP to be signed. +# Right now supported null/false (in order to not sign) or true (sign using SP private key) +onelogin.saml2.security.sign_metadata = true + +# Indicates a requirement for the Assertions received by this SP to be encrypted +onelogin.saml2.security.want_assertions_encrypted = true + +# Indicates a requirement for the NameID received by this SP to be encrypted +onelogin.saml2.security.want_nameid_encrypted = true + +# Authentication context. +# Set Empty and no AuthContext will be sent in the AuthNRequest, +# Set comma separated values urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password +onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:urn:oasis:names:tc:SAML:2.0:ac:classes:Password + +# Allows the authn comparison parameter to be set, defaults to 'exact' +onelogin.saml2.security.requested_authncontextcomparison = exact + + +# Indicates if the SP will validate all received xmls. +# (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). +onelogin.saml2.security.want_xml_validation = true + +# Algorithm that the toolkit will use on signing process. Options: +# 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' +# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' +onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 + +# Algorithm that the toolkit will use on digest process. Options: +# 'http://www.w3.org/2000/09/xmldsig#sha1' +# 'http://www.w3.org/2001/04/xmlenc#sha256' +# 'http://www.w3.org/2001/04/xmldsig-more#sha384' +# 'http://www.w3.org/2001/04/xmlenc#sha512' +onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 + +# Organization +onelogin.saml2.organization.name = SP Java +onelogin.saml2.organization.displayname = SP Java Example +onelogin.saml2.organization.url = http://sp.example.com +onelogin.saml2.organization.lang = en + +# Contacts +onelogin.saml2.contacts.technical.given_name = Technical Guy +onelogin.saml2.contacts.technical.email_address = technical@example.com +onelogin.saml2.contacts.support.given_name = Support Guy +onelogin.saml2.contacts.support.email_address = support@example.com + +# Prefix used in generated Unique IDs. +# Optional, defaults to ONELOGIN_ or full ID is like ONELOGIN_ebb0badd-4f60-4b38-b20a-a8e01f0592b1. +# At minimun, the prefix can be non-numeric character such as "_". +onelogin.saml2.unique_id_prefix = EXAMPLE diff --git a/core/src/test/resources/config/config.all_specialchars.properties b/core/src/test/resources/config/config.all_specialchars.properties index 923297e0..49ed2b43 100644 --- a/core/src/test/resources/config/config.all_specialchars.properties +++ b/core/src/test/resources/config/config.all_specialchars.properties @@ -31,6 +31,40 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified +# Attribute Consuming Service name when just one ACS should be declared by the SP. +# Comment out or set to empty if no ACS should be declared, or if multiple ones should (see below). +# The service name is mandatory. +onelogin.saml2.sp.attribute_consuming_service.name = My s&rvice + +# Attribute Consuming Service description when just one ACS 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 s&rvice description + +# Language used for Attribute Consuming Service name and description when just one ACS 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 = &n + +# Attributes to be included in the Attribute Consuming Service when just one ACS 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&bar@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&LastName + # 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----- diff --git a/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties new file mode 100644 index 00000000..a4ce66ec --- /dev/null +++ b/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties @@ -0,0 +1,62 @@ +# Service Provider Data that we are deploying +# Identifier of the SP entity (must be a URI) +onelogin.saml2.sp.entityid = http://localhost:8080/java-saml-jspsample/metadata.jsp +# Specifies info about where and how the message MUST be +# returned to the requester, in this case our SP. +# URL Location where the from the IdP will be returned +onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-saml-jspsample/acs.jsp + +# Specifies info about Logout service +# URL Location where the from the IdP will be returned or where to send the +onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/java-saml-jspsample/sls.jsp + +# Attributes to be included in the Attribute Consuming Service when just one ACS 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_Wrong +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_wrong@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[1] = bar_wrong@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[1].name = FirstName_Wrong + +# THE FOLLOWING PROPERTIES MUST BE PROCESSED INSTEAD + +onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service[1].name = Anagrafica +onelogin.saml2.sp.attribute_consuming_service[1].description = Servizio completo +onelogin.saml2.sp.attribute_consuming_service[1].lang = it +onelogin.saml2.sp.attribute_consuming_service[1].default = true +onelogin.saml2.sp.attribute_consuming_service[1].attribute[0].name = FirstName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].name = LastName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].required = true + +# 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 From f4724b75907205ddb26054a31e3463d33680cd10 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 30 Mar 2021 19:05:58 +0200 Subject: [PATCH 2/5] Clear out the confusion about the use of ACS acronym ACS is usually used to refer to the Assertion Consumer Service concept in SAML. The ACS may also behave as an Attribute Consuming Services, but in general the two concepts are separate. This fixes the use of the ACS acronym for the Assertion Consumer Service only. --- README.md | 31 ++++++++++--------- .../com/onelogin/saml2/settings/Metadata.java | 2 +- .../saml2/settings/SettingsBuilder.java | 16 +++++----- .../saml2/test/settings/MetadataTest.java | 8 ++--- .../resources/config/config.all.properties | 10 +++--- ...ti_attribute_consuming_services.properties | 12 +++---- .../config/config.all_specialchars.properties | 19 +++++++++--- ...ti_attribute_consuming_services.properties | 2 +- 8 files changed, 56 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index ccbf12b1..043d5bc0 100644 --- a/README.md +++ b/README.md @@ -513,28 +513,28 @@ The getSPMetadata will return the metadata signed or not based on the security p Before the XML metadata is exposed, a check takes place to ensure that the info to be provided is valid. -##### Attribute Consuming Service (ACS) +##### Attribute Consuming Services The SP may optionally specify one or more Attribute Consuming Services in its metadata. These can be configured in the settings. -If just one ACS is required: +If just one Attribute Consuming Service is required: ```properties -# Attribute Consuming Service name when just one ACS should be declared by the SP. -# Comment out or set to empty if no ACS should be declared, or if multiple ones should (see below). +# 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 ACS should be declared by the SP. +# 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 ACS should be declared by the SP. +# 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). +# The language is optional and defaults to "en" (English). onelogin.saml2.sp.attribute_consuming_service.lang = en -# Requested attributes to be included in the Attribute Consuming Service when just one ACS should be declared by the SP. +# Requested attributes to be included in the Attribute Consuming Service when just one such service should be declared by the SP. # At least one requested attribute must be specified, otherwise schema validation will fail. # Attribute properties 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 requested attribute: @@ -542,7 +542,7 @@ onelogin.saml2.sp.attribute_consuming_service.lang = en # - 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 +# - value[x]: an attribute value; the [x] index is 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. @@ -554,9 +554,10 @@ onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[0] = foo@exampl onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[1] = bar@example.org ``` -If multiple ACSs are required, they can be specified in a similar way, but using indexes: these indexes are used to enumerate and -identify attribute consuming services within the SP metadata and can be subsequently used in the auth process to specify which -attribute set should be requested to the IdP. The "default" property can also be set to designate the default ACS. Here is an example: +If multiple Attribute Consuming Services are required, they can be specified in a similar way, but using indexes: these indexes +are used to enumerate and identify attribute consuming services within the SP metadata and can be subsequently used in the auth +process to specify which attribute set should be requested to the IdP. The "default" property can also be set to designate the +default Attribute Consuming Service. Here is an example: ```properties onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail @@ -584,7 +585,7 @@ import static com.onelogin.saml2.authn.AttributeConsumingServiceSelector.*; Auth auth = new Auth(request, response); // select by index 1 auth.login(new AuthnRequestParams(false, false, true, byIndex(1)); -// or select by ACS name +// or select by service name auth.login(new AuthnRequestParams(false, false, true, byServiceName(auth.getSettings(), "Anagrafica")); // or see AttributeConsumingServiceSelector interface implementations for more options ``` @@ -592,7 +593,9 @@ auth.login(new AuthnRequestParams(false, false, true, byServiceName(auth.getSett If no selector is specified, `AttributeConsumingServiceSelector.useDefault()` will be used, which will simply omit any `AttributeConsumingServiceIndex` from the request, hence leaving the IdP choose the default attribute set agreed upon. -Then, the following code handles the SAML response that the IdP forwards to the SP through the user's client: + +##### Assertion Consumer Service (ACS) +This code handles the SAML response that the IdP forwards to the SP through the user's client: ```java Auth auth = new Auth(request, response); 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 6834478a..d8f4405f 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Metadata.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Metadata.java @@ -177,7 +177,7 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) throws Certif valueMap.put("spAssertionConsumerServiceUrl", Util.toXml(settings.getSpAssertionConsumerServiceUrl().toString())); valueMap.put("sls", toSLSXml(settings.getSpSingleLogoutServiceUrl(), settings.getSpSingleLogoutServiceBinding())); - // if an ACS was specified at construction time, use it in place of the ones specified in settings + // if an Attribute Consuming Service was specified at construction time, use it in place of the ones specified in settings // this is for backward compatibility valueMap.put("strAttributeConsumingService", toAttributeConsumingServicesXml(attributeConsumingService != null 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 77217e9d..eda93df6 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -542,17 +542,17 @@ private List loadContacts() { */ private List loadAttributeConsumingServices() { // first split properties into a map of properties - // key = ACS index; value = ACS properties + // key = service index; value = service properties final SortedMap> acsProps = extractIndexedProperties(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX, samlData); - // then build each ACS + // then build each Attribute Consuming Service if(acsProps.containsKey(-1) && acsProps.size() == 1) - // single ACS specified; use index 1 for backward compatibility + // single service specified; use index 1 for backward compatibility return Arrays.asList(loadAttributeConsumingService(acsProps.get(-1), 1)); else - // multiple indexed ACSs specified + // multiple indexed services specified return acsProps.entrySet().stream() - // ignore non-indexed ACS + // ignore non-indexed service .filter(entry -> entry.getKey() != -1) .map(entry -> loadAttributeConsumingService(entry.getValue(), entry.getKey())) .collect(Collectors.toList()); @@ -562,10 +562,10 @@ private List loadAttributeConsumingServices() { * Loads a single Attribute Consuming Service from settings. * * @param acsProps - * a map containing the ACS settings + * a map containing the Attribute Consuming Service settings * @param index - * the index to be set on the returned ACS - * @return the loaded ACS + * the index to be set on the returned Attribute Consuming Service + * @return the loaded Attribute Consuming Service */ private AttributeConsumingService loadAttributeConsumingService(Map acsProps, int index) { final String serviceName = loadStringProperty(SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, acsProps); diff --git a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java index 17b37bc0..514b4b3c 100644 --- a/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java +++ b/core/src/test/java/com/onelogin/saml2/test/settings/MetadataTest.java @@ -721,7 +721,7 @@ public void testToAttributeConsumingServiceXmlWithMultipleAttributeValueLegacySp * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml */ @Test - public void testToAttributeConsumingServiceXmlSingleACS() throws IOException, CertificateEncodingException, Error { + public void testToAttributeConsumingServiceXmlSingleService() throws IOException, CertificateEncodingException, Error { Saml2Settings settings = getSettingFromAllProperties(); Metadata metadataObj = new Metadata(settings, null, null); @@ -792,8 +792,8 @@ public void testToAttributeConsumingServiceXmlSingleACSSpecialChars() throws IOE * @see com.onelogin.saml2.settings.Metadata#toAttributeConsumingServicesXml */ @Test - public void testToAttributeConsumingServiceXmlMultiACS() throws IOException, CertificateEncodingException, Error { - Saml2Settings settings = getSettingFromAllPropertiesMultiACS(); + public void testToAttributeConsumingServiceXmlMultiServices() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllPropertiesMultiAttributeConsumingServices(); Metadata metadataObj = new Metadata(settings, null, null); String metadataStr = metadataObj.getMetadataString(); @@ -927,7 +927,7 @@ private Saml2Settings getSettingFromAllSpecialCharsProperties() throws Error, IO return new SettingsBuilder().fromFile("config/config.all_specialchars.properties").build(); } - private Saml2Settings getSettingFromAllPropertiesMultiACS() throws Error, IOException { + private Saml2Settings getSettingFromAllPropertiesMultiAttributeConsumingServices() throws Error, IOException { return new SettingsBuilder().fromFile("config/config.all_multi_attribute_consuming_services.properties").build(); } diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index ce8a7a83..10a50bb2 100644 --- a/core/src/test/resources/config/config.all.properties +++ b/core/src/test/resources/config/config.all.properties @@ -31,22 +31,22 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified -# Attribute Consuming Service name when just one ACS should be declared by the SP. -# Comment out or set to empty if no ACS should be declared, or if multiple ones should (see below). +# 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 ACS should be declared by the SP. +# 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 ACS should be declared by the SP. +# 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 ACS should be declared by the SP. +# 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 diff --git a/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties index 160dfda9..cc3e8ed2 100644 --- a/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties +++ b/core/src/test/resources/config/config.all_multi_attribute_consuming_services.properties @@ -31,24 +31,24 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified -# THE FOLLOWING PROPERTIES FOR SINGLE ACS MUST BE IGNORED - MULTIPLE SERVICES DEFINED LATER +# THE FOLLOWING PROPERTIES FOR SINGLE ATTRIBUTE CONSUMING SERVICE MUST BE IGNORED - MULTIPLE SERVICES DEFINED LATER -# Attribute Consuming Service name when just one ACS should be declared by the SP. -# Comment out or set to empty if no ACS should be declared, or if multiple ones should (see below). +# 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 ACS should be declared by the SP. +# 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 ACS should be declared by the SP. +# 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 ACS should be declared by the SP. +# 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 diff --git a/core/src/test/resources/config/config.all_specialchars.properties b/core/src/test/resources/config/config.all_specialchars.properties index 49ed2b43..b64bffd7 100644 --- a/core/src/test/resources/config/config.all_specialchars.properties +++ b/core/src/test/resources/config/config.all_specialchars.properties @@ -31,22 +31,22 @@ onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bi # Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified -# Attribute Consuming Service name when just one ACS should be declared by the SP. -# Comment out or set to empty if no ACS should be declared, or if multiple ones should (see below). +# 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 s&rvice -# Attribute Consuming Service description when just one ACS should be declared by the SP. +# 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 s&rvice description -# Language used for Attribute Consuming Service name and description when just one ACS should be declared by the SP. +# 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 = &n -# Attributes to be included in the Attribute Consuming Service when just one ACS should be declared by the SP. +# 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 @@ -171,6 +171,15 @@ onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig- # 'http://www.w3.org/2001/04/xmlenc#sha512' onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha512 +# Enable trimming of parsed Name IDs and attribute values +# SAML specification states that no trimming for string elements should be performed, so no trimming will be +# performed by default on extracted Name IDs and attribute values. However, some SAML implementations may add +# undesirable surrounding whitespace when outputting XML (possibly due to formatting/pretty-printing). +# These two options allow to optionally enable value trimming on extracted Name IDs (including issuers) and +# attribute values. +onelogin.saml2.parsing.trim_name_ids = false +onelogin.saml2.parsing.trim_attribute_values = false + # Organization onelogin.saml2.organization.name = S&P Java onelogin.saml2.organization.displayname = S&P Java "Example" diff --git a/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties index a4ce66ec..db6a0050 100644 --- a/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties +++ b/core/src/test/resources/config/config.min_multi_attribute_consuming_services.properties @@ -10,7 +10,7 @@ onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/java-sa # 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 -# Attributes to be included in the Attribute Consuming Service when just one ACS should be declared by the SP. +# 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 From 3bb0d9e8b1b9cf1b96de42c46844e3812cf22a4a Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 9 Apr 2021 12:12:05 +0200 Subject: [PATCH 3/5] Add Attribute Consuming Services tests to SettingsBuilderTest --- .../model/AttributeConsumingService.java | 17 +- .../test/settings/SettingBuilderTest.java | 259 ++++++++++++++++++ 2 files changed, 269 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java b/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java index 3429bf93..1ed53201 100644 --- a/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java +++ b/core/src/main/java/com/onelogin/saml2/model/AttributeConsumingService.java @@ -57,7 +57,7 @@ public AttributeConsumingService(int index, Boolean isDefault, String serviceNam this.index = index; this.isDefault = isDefault; this.serviceName = serviceName != null? serviceName : ""; - this.serviceDescription = serviceDescription != null? serviceDescription : ""; + this.serviceDescription = serviceDescription; this.lang = lang != null? lang: "en"; this.requestedAttributes = new ArrayList(); } @@ -79,18 +79,21 @@ public AttributeConsumingService(int index, Boolean isDefault, String serviceNam } /** - * Constructor for a non-default attribute consuming service with index 1 - * and service name and descriptions in English. - * Mainly kept for backward compatibility, this constructor can be used when an only - * attribute consuming service is required. + * Constructor for a non-default attribute consuming service with index + * 1 and service name and descriptions in English. + *

+ * Mainly kept for backward compatibility, this constructor can be used when an + * only attribute consuming service is required. Please also note that, to + * maintain full backward compatibility, if the service description is + * null this constructor will set is as an empty string. * * @param serviceName * String. Service Name * @param serviceDescription - * String. Service Description + * String. Service Description; if null, an empty string will be set */ public AttributeConsumingService(String serviceName, String serviceDescription) { - this(1, null, serviceName, serviceDescription, null); + this(1, null, serviceName, serviceDescription != null? serviceDescription : "", null); } /** 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..057b5a31 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,9 +33,11 @@ import com.onelogin.saml2.exception.Error; import com.onelogin.saml2.exception.SettingsException; +import com.onelogin.saml2.model.AttributeConsumingService; import com.onelogin.saml2.model.Contact; import com.onelogin.saml2.model.KeyStoreSettings; import com.onelogin.saml2.model.Organization; +import com.onelogin.saml2.model.RequestedAttribute; import com.onelogin.saml2.settings.Saml2Settings; import com.onelogin.saml2.settings.SettingsBuilder; import com.onelogin.saml2.util.Constants; @@ -135,6 +137,7 @@ public void testLoadFromFileEmpty() throws IOException, CertificateException, UR assertNull(setting.getSpSingleLogoutServiceUrl()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", setting.getSpSingleLogoutServiceBinding()); assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", setting.getSpNameIDFormat()); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertTrue(setting.getIdpEntityId().isEmpty()); assertNull(setting.getIdpSingleSignOnServiceUrl()); @@ -193,6 +196,7 @@ public void testLoadFromFileMinProp() throws IOException, CertificateException, 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"); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -254,6 +258,31 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, 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"); + final List attributeConsumingServices = setting.getSpAttributeConsumingServices(); + assertEquals(1, attributeConsumingServices.size()); + final AttributeConsumingService attributeConsumingService = attributeConsumingServices.get(0); + assertEquals(1, attributeConsumingService.getIndex()); + assertNull(attributeConsumingService.isDefault()); + assertEquals("My service", attributeConsumingService.getServiceName()); + assertEquals("My service description", attributeConsumingService.getServiceDescription()); + assertEquals("en", attributeConsumingService.getLang()); + final List requestedAttributes = attributeConsumingService.getRequestedAttributes(); + assertEquals(2, requestedAttributes.size()); + final RequestedAttribute requestedAttribute1 = requestedAttributes.get(0); + assertEquals("Email", requestedAttribute1.getName()); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", requestedAttribute1.getNameFormat()); + assertEquals("E-mail address", requestedAttribute1.getFriendlyName()); + assertTrue(requestedAttribute1.isRequired()); + final List requestedAttribute1Values = requestedAttribute1.getAttributeValues(); + assertEquals(2, requestedAttribute1Values.size()); + assertEquals("foo@example.org", requestedAttribute1Values.get(0)); + assertEquals("bar@example.org", requestedAttribute1Values.get(1)); + final RequestedAttribute requestedAttribute2 = requestedAttributes.get(1); + assertEquals("FirstName", requestedAttribute2.getName()); + assertNull(requestedAttribute2.getNameFormat()); + assertNull(requestedAttribute2.getFriendlyName()); + assertNull(requestedAttribute2.isRequired()); + assertTrue(requestedAttribute2.getAttributeValues().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -336,6 +365,72 @@ public void testLoadFromFileAllProp() throws IOException, CertificateException, assertEquals("EXAMPLE", setting.getUniqueIDPrefix()); } + /** + * Tests SettingsBuilder fromFile method + *

+ * Case: all settings config file with multiple Attribute Consuming Services + * + * @throws IOException + * @throws CertificateException + * @throws URISyntaxException + * @throws SettingsException + * @throws Error + * + * @see com.onelogin.saml2.settings.SettingsBuilder#fromFile + */ + @Test + public void testLoadFromFileAllPropMultiAttributeConsumingServices() throws IOException, CertificateException, URISyntaxException, SettingsException, Error { + Saml2Settings setting = new SettingsBuilder().fromFile("config/config.all_multi_attribute_consuming_services.properties").build(); + + // let's test only the Attribute Consuming Service part - no need to test again all the rest + final List attributeConsumingServices = setting.getSpAttributeConsumingServices(); + assertEquals(2, attributeConsumingServices.size()); + + { + final AttributeConsumingService attributeConsumingService1 = attributeConsumingServices.get(0); + assertEquals(0, attributeConsumingService1.getIndex()); + assertNull(attributeConsumingService1.isDefault()); + assertEquals("Just e-mail", attributeConsumingService1.getServiceName()); + assertNull(attributeConsumingService1.getServiceDescription()); + assertEquals("en", attributeConsumingService1.getLang()); + final List requestedAttributes1 = attributeConsumingService1.getRequestedAttributes(); + assertEquals(1, requestedAttributes1.size()); + final RequestedAttribute requestedAttribute11 = requestedAttributes1.get(0); + assertEquals("Email", requestedAttribute11.getName()); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", requestedAttribute11.getNameFormat()); + assertEquals("E-mail address", requestedAttribute11.getFriendlyName()); + assertTrue(requestedAttribute11.isRequired()); + final List requestedAttribute11Values = requestedAttribute11.getAttributeValues(); + assertEquals(2, requestedAttribute11Values.size()); + assertEquals("foo@example.org", requestedAttribute11Values.get(0)); + assertEquals("bar@example.org", requestedAttribute11Values.get(1)); + } + + { + final AttributeConsumingService attributeConsumingService2 = attributeConsumingServices.get(1); + assertEquals(1, attributeConsumingService2.getIndex()); + assertTrue(attributeConsumingService2.isDefault()); + assertEquals("Anagrafica \"completa\"", attributeConsumingService2.getServiceName()); + assertEquals("Dati anagrafici \"completi\"", attributeConsumingService2.getServiceDescription()); + assertEquals("it", attributeConsumingService2.getLang()); + final List requestedAttributes2 = attributeConsumingService2.getRequestedAttributes(); + assertEquals(2, requestedAttributes2.size()); + final RequestedAttribute requestedAttribute21 = requestedAttributes2.get(0); + assertEquals("Birth date", requestedAttribute21.getName()); + assertNull(requestedAttribute21.getNameFormat()); + assertNull(requestedAttribute21.getFriendlyName()); + assertNull(requestedAttribute21.isRequired()); + final List requestedAttribute21Values = requestedAttribute21.getAttributeValues(); + assertTrue(requestedAttribute21Values.isEmpty()); + final RequestedAttribute requestedAttribute22 = requestedAttributes2.get(1); + assertEquals("First & Last Name", requestedAttribute22.getName()); + assertNull(requestedAttribute22.getNameFormat()); + assertNull(requestedAttribute22.getFriendlyName()); + assertTrue(requestedAttribute22.isRequired()); + assertTrue(requestedAttribute22.getAttributeValues().isEmpty()); + } + } + /** * Tests SettingsBuilder fromFile method * Case: settings config file with certificate string @@ -361,6 +456,7 @@ public void testLoadFromFileCertString() throws IOException, CertificateExceptio assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", setting.getSpSingleLogoutServiceBinding()); assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", setting.getSpNameIDFormat()); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -416,6 +512,7 @@ public void testLoadFromFileContactString() throws IOException, CertificateExcep assertEquals("http://localhost:8080/java-saml-jspsample/sls.jsp", setting.getSpSingleLogoutServiceUrl().toString()); assertEquals("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", setting.getSpSingleLogoutServiceBinding()); assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", setting.getSpNameIDFormat()); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -527,6 +624,7 @@ public void testLoadFromFileSomeEmptyProp() throws IOException, CertificateExcep 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"); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -581,6 +679,7 @@ public void testLoadFromFileDifferentProp() throws IOException, CertificateExcep 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"); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -679,6 +778,7 @@ public void testFromProperties() throws IOException, Error, CertificateException 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"); + assertTrue(setting.getSpAttributeConsumingServices().isEmpty()); assertEquals("http://idp.example.com/", setting2.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting2.getIdpSingleSignOnServiceUrl().toString()); @@ -739,6 +839,16 @@ public void testLoadFromValues() throws Exception { samlData.put(SP_X509CERT_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_X509CERTNEW_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_PRIVATEKEY_PROPERTY_KEY, "-----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY-----"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, "My service"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX, "My service description"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX, "en"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "Email"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX, "E-mail address"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, "true"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[0]", "foo@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[1]", "bar@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "FirstName"); // Build IdP samlData.put(IDP_ENTITYID_PROPERTY_KEY, "http://idp.example.com/"); @@ -817,6 +927,31 @@ public void testLoadFromValues() throws Exception { assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); assertNotNull(setting.getSPcert()); assertNotNull(setting.getSPcertNew()); + final List attributeConsumingServices = setting.getSpAttributeConsumingServices(); + assertEquals(1, attributeConsumingServices.size()); + final AttributeConsumingService attributeConsumingService = attributeConsumingServices.get(0); + assertEquals(1, attributeConsumingService.getIndex()); + assertNull(attributeConsumingService.isDefault()); + assertEquals("My service", attributeConsumingService.getServiceName()); + assertEquals("My service description", attributeConsumingService.getServiceDescription()); + assertEquals("en", attributeConsumingService.getLang()); + final List requestedAttributes = attributeConsumingService.getRequestedAttributes(); + assertEquals(2, requestedAttributes.size()); + final RequestedAttribute requestedAttribute1 = requestedAttributes.get(0); + assertEquals("Email", requestedAttribute1.getName()); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", requestedAttribute1.getNameFormat()); + assertEquals("E-mail address", requestedAttribute1.getFriendlyName()); + assertTrue(requestedAttribute1.isRequired()); + final List requestedAttribute1Values = requestedAttribute1.getAttributeValues(); + assertEquals(2, requestedAttribute1Values.size()); + assertEquals("foo@example.org", requestedAttribute1Values.get(0)); + assertEquals("bar@example.org", requestedAttribute1Values.get(1)); + final RequestedAttribute requestedAttribute2 = requestedAttributes.get(1); + assertEquals("FirstName", requestedAttribute2.getName()); + assertNull(requestedAttribute2.getNameFormat()); + assertNull(requestedAttribute2.getFriendlyName()); + assertNull(requestedAttribute2.isRequired()); + assertTrue(requestedAttribute2.getAttributeValues().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -951,6 +1086,16 @@ public void testLoadFromValuesWithObjects() throws Exception { samlData.put(SP_X509CERT_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_X509CERTNEW_PROPERTY_KEY, "-----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE-----"); samlData.put(SP_PRIVATEKEY_PROPERTY_KEY, "-----BEGIN PRIVATE KEY-----MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAECgYA7VPVRl+/xoVeWdKdWY1F17HerSa23ynI2vQ8TkUY6kR3ucz6ElRxHJesY8fNCPoX+XuMfUly7IKyPZMkWyvEgDPo7J5mYqP5VsTK0Li4AwR/BA93Aw6gaX7/EYi3HjBh8QdNSt4fi9yOea/hv04yfR9Lx/a5fvQIyhqaDtT2QeQJBAOnCgnxnj70/sv9UsFPa8t1OGdAfXtOgEoklh1F2NR9jid6FPw5E98eCpdZ00MfRrmUavgqg6Y4swZISyzJIjGMCQQDN0YNsC4S+eJJM6aOCpupKluWE/cCWB01UQYekyXH7OdUtl49NlKEUPBSAvtaLMuMKlTNOjlPrx4Q+/c5i0vTPAkEA5H7CR9J/OZETaewhc8ZYkaRvLPYNHjWhCLhLXoB6itUkhgOfUFZwEXAOpOOI1VmL675JN2B1DAmJqTx/rQYnWwJBAMx3ztsAmnBq8dTM6y65ydouDHhRawjg2jbRHwNbSQvuyVSQ08Gb3WZvxWKdtB/3fsydqqnpBYAf5sZ5eJZ+wssCQAOiIKnhdYe+RBbBwykzjUqtzEmt4fwCFE8tD4feEx77D05j5f7u7KYh1mL0G2zIbnUryi7jwc4ye98VirRpZ1w=-----END PRIVATE KEY-----"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, "My service"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX, "My service description"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX, "en"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "Email"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX, "E-mail address"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, Boolean.TRUE); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[0]", "foo@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[1]", "bar@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "FirstName"); // Build IdP samlData.put(IDP_ENTITYID_PROPERTY_KEY, "http://idp.example.com/"); @@ -1025,6 +1170,31 @@ public void testLoadFromValuesWithObjects() throws Exception { assertEquals(setting.getSpNameIDFormat(), "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"); assertNotNull(setting.getSPcert()); assertNotNull(setting.getSPcertNew()); + final List attributeConsumingServices = setting.getSpAttributeConsumingServices(); + assertEquals(1, attributeConsumingServices.size()); + final AttributeConsumingService attributeConsumingService = attributeConsumingServices.get(0); + assertEquals(1, attributeConsumingService.getIndex()); + assertNull(attributeConsumingService.isDefault()); + assertEquals("My service", attributeConsumingService.getServiceName()); + assertEquals("My service description", attributeConsumingService.getServiceDescription()); + assertEquals("en", attributeConsumingService.getLang()); + final List requestedAttributes = attributeConsumingService.getRequestedAttributes(); + assertEquals(2, requestedAttributes.size()); + final RequestedAttribute requestedAttribute1 = requestedAttributes.get(0); + assertEquals("Email", requestedAttribute1.getName()); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", requestedAttribute1.getNameFormat()); + assertEquals("E-mail address", requestedAttribute1.getFriendlyName()); + assertTrue(requestedAttribute1.isRequired()); + final List requestedAttribute1Values = requestedAttribute1.getAttributeValues(); + assertEquals(2, requestedAttribute1Values.size()); + assertEquals("foo@example.org", requestedAttribute1Values.get(0)); + assertEquals("bar@example.org", requestedAttribute1Values.get(1)); + final RequestedAttribute requestedAttribute2 = requestedAttributes.get(1); + assertEquals("FirstName", requestedAttribute2.getName()); + assertNull(requestedAttribute2.getNameFormat()); + assertNull(requestedAttribute2.getFriendlyName()); + assertNull(requestedAttribute2.isRequired()); + assertTrue(requestedAttribute2.getAttributeValues().isEmpty()); assertEquals("http://idp.example.com/", setting.getIdpEntityId()); assertEquals("http://idp.example.com/simplesaml/saml2/idp/SSOService.php", setting.getIdpSingleSignOnServiceUrl().toString()); @@ -1112,6 +1282,95 @@ public void testLoadFromValuesWithObjects() throws Exception { assertEquals("ONELOGIN_", setting.getUniqueIDPrefix()); } + /** + * Tests SettingsBuilder constructor + * Case: settings from values when multiple Attribute Consuming Services are defined + * + * @throws IOException + * + * @see com.onelogin.saml2.settings.SettingsBuilder + */ + @Test + public void testLoadFromValuesMultiAttributeConsumingServices() throws Exception { + Map samlData = new LinkedHashMap<>(); + + // just build Attribute Consuming Services + // the following must be ignored, because indexed properties are present + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, "My service"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX, "My service description"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX, "en"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "Email"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX, "E-mail address"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, Boolean.TRUE); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[0]", "foo@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[1]", "bar@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "FirstName"); + // the following, instead, must be processed + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, "Just e-mail"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "Email"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_FORMAT_PROPERTY_KEY_SUFFIX, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_FRIENDLY_NAME_PROPERTY_KEY_SUFFIX, "E-mail address"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, "true"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[0]", "foo@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_VALUE_PROPERTY_KEY_PREFIX + "[1]", "bar@example.org"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_NAME_PROPERTY_KEY_SUFFIX, "Anagrafica"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_DESCRIPTION_PROPERTY_KEY_SUFFIX, "Servizio completo"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_LANG_PROPERTY_KEY_SUFFIX, "it"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_DEFAULT_PROPERTY_KEY_SUFFIX, "true"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[0]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "FirstName"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_NAME_PROPERTY_KEY_SUFFIX, "LastName"); + samlData.put(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_PROPERTY_KEY_PREFIX + "[1]." + SP_ATTRIBUTE_CONSUMING_SERVICE_ATTRIBUTE_REQUIRED_PROPERTY_KEY_SUFFIX, "true"); + + Saml2Settings setting = new SettingsBuilder().fromValues(samlData).build(); + + final List attributeConsumingServices = setting.getSpAttributeConsumingServices(); + assertEquals(2, attributeConsumingServices.size()); + + { + final AttributeConsumingService attributeConsumingService1 = attributeConsumingServices.get(0); + assertEquals(0, attributeConsumingService1.getIndex()); + assertNull(attributeConsumingService1.isDefault()); + assertEquals("Just e-mail", attributeConsumingService1.getServiceName()); + assertNull(attributeConsumingService1.getServiceDescription()); + assertEquals("en", attributeConsumingService1.getLang()); + final List requestedAttributes1 = attributeConsumingService1.getRequestedAttributes(); + assertEquals(1, requestedAttributes1.size()); + final RequestedAttribute requestedAttribute11 = requestedAttributes1.get(0); + assertEquals("Email", requestedAttribute11.getName()); + assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", requestedAttribute11.getNameFormat()); + assertEquals("E-mail address", requestedAttribute11.getFriendlyName()); + assertTrue(requestedAttribute11.isRequired()); + final List requestedAttribute11Values = requestedAttribute11.getAttributeValues(); + assertEquals(2, requestedAttribute11Values.size()); + assertEquals("foo@example.org", requestedAttribute11Values.get(0)); + assertEquals("bar@example.org", requestedAttribute11Values.get(1)); + } + + { + final AttributeConsumingService attributeConsumingService2 = attributeConsumingServices.get(1); + assertEquals(1, attributeConsumingService2.getIndex()); + assertTrue(attributeConsumingService2.isDefault()); + assertEquals("Anagrafica", attributeConsumingService2.getServiceName()); + assertEquals("Servizio completo", attributeConsumingService2.getServiceDescription()); + assertEquals("it", attributeConsumingService2.getLang()); + final List requestedAttributes2 = attributeConsumingService2.getRequestedAttributes(); + assertEquals(2, requestedAttributes2.size()); + final RequestedAttribute requestedAttribute21 = requestedAttributes2.get(0); + assertEquals("FirstName", requestedAttribute21.getName()); + assertNull(requestedAttribute21.getNameFormat()); + assertNull(requestedAttribute21.getFriendlyName()); + assertNull(requestedAttribute21.isRequired()); + final List requestedAttribute21Values = requestedAttribute21.getAttributeValues(); + assertTrue(requestedAttribute21Values.isEmpty()); + final RequestedAttribute requestedAttribute22 = requestedAttributes2.get(1); + assertEquals("LastName", requestedAttribute22.getName()); + assertNull(requestedAttribute22.getNameFormat()); + assertNull(requestedAttribute22.getFriendlyName()); + assertTrue(requestedAttribute22.isRequired()); + assertTrue(requestedAttribute22.getAttributeValues().isEmpty()); + } + } /** * Tests SettingsBuilder constructor * Case: settings config file with certificate loaded from file From 4978a18662ded86ff9a2615c9df4a3cf90a85238 Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Fri, 9 Apr 2021 18:19:09 +0200 Subject: [PATCH 4/5] Add Attribute Consuming Services settings check Now Saml2Settings.checkSPSettings() also checks that, if any Attribute Consuming Service are declared, their configuration is consistent. --- .../saml2/settings/Saml2Settings.java | 40 +++++++++ .../test/settings/Saml2SettingsTest.java | 82 +++++++++++++++++++ ...ti_attribute_consuming_services.properties | 40 +++++++++ ...ti_attribute_consuming_services.properties | 51 ++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 core/src/test/resources/config/config.allerrors_multi_attribute_consuming_services.properties create mode 100644 core/src/test/resources/config/config.sperrors_multi_attribute_consuming_services.properties 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 103f271c..50210f63 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java +++ b/core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java @@ -1009,6 +1009,44 @@ private boolean checkIdpx509certRequired () { return this.getIdpx509certMulti() != null && !this.getIdpx509certMulti().isEmpty(); } + + /* + * Auxiliary method to check Attribute Consuming Services are properly + * configured. + * + * @param errors the list to add to when an error is encountered + */ + private void checkAttributeConsumingServices(List errors) { + List attributeConsumingServices = getSpAttributeConsumingServices(); + if(!attributeConsumingServices.isEmpty()) { + String errorMsg; + // all Attribute Consuming Services must have a service name + if(attributeConsumingServices.stream().anyMatch(service -> StringUtils.isEmpty(service.getServiceName()))) { + errorMsg = "sp_attribute_consuming_service_not_enough_data"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + // all Attribute Consuming Services must have at least one requested attribute + if(attributeConsumingServices.stream().anyMatch(service -> service.getRequestedAttributes().isEmpty())) { + errorMsg = "sp_attribute_consuming_service_no_requested_attribute"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + // there must be at most one with default = true + if(attributeConsumingServices.stream().filter(service -> Boolean.TRUE.equals(service.isDefault())).count() > 1) { + errorMsg = "sp_attribute_consuming_service_multiple_defaults"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + // all requested attributes must have a name + if(attributeConsumingServices.stream().flatMap(service -> service.getRequestedAttributes().stream()) + .anyMatch(attribute -> StringUtils.isEmpty(attribute.getName()))) { + errorMsg = "sp_attribute_consuming_service_not_enough_requested_attribute_data"; + errors.add(errorMsg); + LOGGER.error(errorMsg); + } + } + } /** * Checks the SP settings . @@ -1030,6 +1068,8 @@ public List checkSPSettings() { errors.add(errorMsg); LOGGER.error(errorMsg); } + + checkAttributeConsumingServices(errors); if (this.getHsm() == null && (this.getAuthnRequestsSigned() || this.getLogoutRequestSigned() || this.getLogoutResponseSigned() || this.getWantAssertionsEncrypted() || this.getWantNameIdEncrypted()) && !this.checkSPCerts()) { 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..c8f5ed11 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 @@ -117,6 +117,19 @@ 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_attribute_consuming_services.properties").build(); + List settings2Errors = settings2.checkSPSettings(); + assertFalse(settings2Errors.isEmpty()); + assertThat(settings2Errors, hasItem("sp_entityId_not_found")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_not_enough_data")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_no_requested_attribute")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_multiple_defaults")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_not_enough_requested_attribute_data")); + 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 +146,15 @@ 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_attribute_consuming_services.properties").build(); + List settings2Errors = settings2.checkSPSettings(); + assertTrue(settings2Errors.isEmpty()); + + // no attribute consuming services at all + Saml2Settings settings3 = new SettingsBuilder().fromFile("config/config.min.properties").build(); + List settings3Errors = settings3.checkSPSettings(); + assertTrue(settings3Errors.isEmpty()); } /** @@ -159,6 +181,23 @@ 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_attribute_consuming_services.properties").build(); + List settings2Errors = settings2.checkSettings(); + assertFalse(settings2Errors.isEmpty()); + assertThat(settings2Errors, hasItem("sp_entityId_not_found")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_not_enough_data")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_no_requested_attribute")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_multiple_defaults")); + assertThat(settings2Errors, hasItem("sp_attribute_consuming_service_not_enough_requested_attribute_data")); + 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")); } /** @@ -287,6 +326,49 @@ 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 Attribute Consuming Services + * + * @throws Exception + * + * @see com.onelogin.saml2.settings.Saml2Settings#getSPMetadata + */ + @Test + public void testGetSPMetadataUnsignedMultiAttributeConsumingServices() throws Exception { + Saml2Settings settings = new SettingsBuilder().fromFile("config/config.min_multi_attribute_consuming_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("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("Just e-mail")); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("foo@example.org")); + assertThat(metadataStr, containsString("bar@example.org")); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("Anagrafica")); + assertThat(metadataStr, containsString("Servizio completo")); + assertThat(metadataStr, containsString("")); + assertThat(metadataStr, containsString("")); + } + /** * Tests the getSPMetadata method of the Saml2Settings * * Case Unsigned metadata No SLS diff --git a/core/src/test/resources/config/config.allerrors_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.allerrors_multi_attribute_consuming_services.properties new file mode 100644 index 00000000..d4ff9352 --- /dev/null +++ b/core/src/test/resources/config/config.allerrors_multi_attribute_consuming_services.properties @@ -0,0 +1,40 @@ +# we have some Attribute Consuming Services with missing information and multiple defaults +onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail +onelogin.saml2.sp.attribute_consuming_service[0].default = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name = +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service[1].name = +onelogin.saml2.sp.attribute_consuming_service[1].default = true +onelogin.saml2.sp.attribute_consuming_service[1].attribute[0].name = FirstName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].name = LastName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].required = true +onelogin.saml2.sp.attribute_consuming_service[2].name = No requested attributes + +# 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.sperrors_multi_attribute_consuming_services.properties b/core/src/test/resources/config/config.sperrors_multi_attribute_consuming_services.properties new file mode 100644 index 00000000..267c8d35 --- /dev/null +++ b/core/src/test/resources/config/config.sperrors_multi_attribute_consuming_services.properties @@ -0,0 +1,51 @@ +# 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 + +# we have some Attribute Consuming Services with missing information and multiple defaults +onelogin.saml2.sp.attribute_consuming_service[0].name = Just e-mail +onelogin.saml2.sp.attribute_consuming_service[0].default = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name = +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service[0].attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service[1].name = +onelogin.saml2.sp.attribute_consuming_service[1].default = true +onelogin.saml2.sp.attribute_consuming_service[1].attribute[0].name = FirstName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].name = LastName +onelogin.saml2.sp.attribute_consuming_service[1].attribute[1].required = true +onelogin.saml2.sp.attribute_consuming_service[2].name = No requested attributes + +# 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 From 2eb29ba57f774ace51a1083068ff1bcc92d436ed Mon Sep 17 00:00:00 2001 From: Mauro Molinari Date: Tue, 17 Aug 2021 17:54:58 +0200 Subject: [PATCH 5/5] Add warning log when ignoring a non-indexed Attribute Consuming Service When a non-indexed Attribute Consuming Service is defined in configuration along with other indexed Services, a warning is printed to the log to inform that the non-indexed one will be ignored. --- .../java/com/onelogin/saml2/settings/SettingsBuilder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 eda93df6..3a6ff501 100644 --- a/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java +++ b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java @@ -553,7 +553,13 @@ private List loadAttributeConsumingServices() { // multiple indexed services specified return acsProps.entrySet().stream() // ignore non-indexed service - .filter(entry -> entry.getKey() != -1) + .filter(entry -> { + final boolean indexed = entry.getKey() != -1; + if(!indexed) { + LOGGER.warn("non indexed Attribute Consuming Service found along with other indexed Services; the non-indexed one will be ignored"); + } + return indexed; + }) .map(entry -> loadAttributeConsumingService(entry.getValue(), entry.getKey())) .collect(Collectors.toList()); }