diff --git a/README.md b/README.md index df0c3bf0..043d5bc0 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,90 @@ 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 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 Attribute Consuming Service is required: + +```properties +# Attribute Consuming Service name when just one such service should be declared by the SP. +# Comment out or set to empty if no Attribute Consuming Service should be declared, or if multiple ones should (see below). +# The service name is mandatory. +onelogin.saml2.sp.attribute_consuming_service.name = My service + +# Attribute Consuming Service description when just one such service should be declared by the SP. +# Ignored if the previous property is commented or empty. +# The service description is optional. +onelogin.saml2.sp.attribute_consuming_service.description = My service description + +# Language used for Attribute Consuming Service name and description when just one such service should be declared by the SP. +# Ignored if the name property is commented or empty. +# The language is optional and 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 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: +# - 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] 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. +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 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 +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 service 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. + + +##### 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); 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..1ed53201 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,62 @@ 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.serviceDescription = 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. 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; if null, an empty string will be set + */ + public AttributeConsumingService(String serviceName, String serviceDescription) { + this(1, null, serviceName, serviceDescription != null? serviceDescription : "", null); + } + /** * @param attr * RequestedAttribute. The requested attribute to be included @@ -47,6 +104,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 +132,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..d8f4405f 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 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 + ? 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..50210f63 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 @@ -988,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 . @@ -1009,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/main/java/com/onelogin/saml2/settings/SettingsBuilder.java b/core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java index 6232044e..3a6ff501 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,81 @@ 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 = service index; value = service properties + final SortedMap> acsProps = + extractIndexedProperties(SP_ATTRIBUTE_CONSUMING_SERVICE_PROPERTY_KEY_PREFIX, samlData); + // then build each Attribute Consuming Service + if(acsProps.containsKey(-1) && acsProps.size() == 1) + // single service specified; use index 1 for backward compatibility + return Arrays.asList(loadAttributeConsumingService(acsProps.get(-1), 1)); + else + // multiple indexed services specified + return acsProps.entrySet().stream() + // ignore non-indexed service + .filter(entry -> { + final boolean indexed = entry.getKey() != -1; + if(!indexed) { + LOGGER.warn("non indexed 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()); + } + + /** + * Loads a single Attribute Consuming Service from settings. + * + * @param acsProps + * a map containing the Attribute Consuming Service settings + * @param index + * 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); + 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 +823,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 +892,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 testToAttributeConsumingServiceXmlSingleService() 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 testToAttributeConsumingServiceXmlMultiServices() throws IOException, CertificateEncodingException, Error { + Saml2Settings settings = getSettingFromAllPropertiesMultiAttributeConsumingServices(); + + 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 getSettingFromAllPropertiesMultiAttributeConsumingServices() 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/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/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 diff --git a/core/src/test/resources/config/config.all.properties b/core/src/test/resources/config/config.all.properties index fcdbf242..10a50bb2 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 such service should be declared by the SP. +# Comment out or set to empty if no Attribute Consuming Service should be declared, or if multiple ones should (see below). +# The service name is mandatory. +onelogin.saml2.sp.attribute_consuming_service.name = My service + +# Attribute Consuming Service description when just one such service should be declared by the SP. +# Ignored if the previous property is commented or empty. +# The service description is optional. +onelogin.saml2.sp.attribute_consuming_service.description = My service description + +# Language used for Attribute Consuming Service name and description when just one such service should be declared by the SP. +# Ignored if the name property is commented or empty. +# The language is optional and default to "en" (English). +onelogin.saml2.sp.attribute_consuming_service.lang = en + +# Attributes to be included in the Attribute Consuming Service when just one such service should be declared by the SP. +# These are indexed properties, starting from 0. The index is used only to enumerate and sort attributes, but it's required. +# The following properties allow to define each attribute: +# - name: mandatory +# - name_format: optional; if omitted, defaults to urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified +# - friendly_name: optional; if omitted, it won't appear in SP metadata +# - required: optional; if omitted or empty, defaults to false +# - value[x]: an attribute value; the [x] is only used only to enumerate and sort values, but it's required +# Please note that only simple values are currently supported and treated internally as strings. Hence no structured values +# and no ability to specify an xsi:type attribute. +# Attribute values are optional and most often they are simply omitted. +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service.attribute[0].friendly_name = E-mail address +onelogin.saml2.sp.attribute_consuming_service.attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[0] = foo@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[1] = bar@example.org +onelogin.saml2.sp.attribute_consuming_service.attribute[1].name = FirstName + # Usually x509cert and privateKey of the SP are provided by files placed at # the certs folder. But we can also provide them with the following parameters onelogin.saml2.sp.x509cert = -----BEGIN CERTIFICATE-----MIICeDCCAeGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wHhcNMTUxMDE4MjAxMjM1WhcNMTgwNzE0MjAxMjM1WjBZMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lTG9naW4gSW5jMR4wHAYDVQQDDBVqYXZhLXNhbWwuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALvwEktX1+4y2AhEqxVwOO6HO7Wtzi3hr5becRkfLYGjNSyhzZCjI1DsNL61JSWDO3nviZd9fSkFnRC4akFUm0CS6GJ7TZe4T5o+9aowQ6N8e8cts9XPXyP6Inz7q4sD8pO2EInlfwHYPQCqFmz/SDW7cDgIC8vb0ygOsiXdreANAgMBAAGjUDBOMB0GA1UdDgQWBBTifMwN3CQ5ZOPkV5tDJsutU8teFDAfBgNVHSMEGDAWgBTifMwN3CQ5ZOPkV5tDJsutU8teFDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAG3nAEUjJaA75SkzID5FKLolsxG5TE/0HU0+yEUAVkXiqvqN4mPWq/JjoK5+uP4LEZIb4pRrCqI3iHp+vazLLYSeyV3kaGN7q35Afw8nk8WM0f7vImbQ69j1S8GQ+6E0PEI26qBLykGkMn3GUVtBBWSdpP093NuNLJiOomnHqhqj-----END CERTIFICATE----- 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..cc3e8ed2 --- /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 ATTRIBUTE CONSUMING SERVICE MUST BE IGNORED - MULTIPLE SERVICES DEFINED LATER + +# Attribute Consuming Service name when just one such service should be declared by the SP. +# Comment out or set to empty if no Attribute Consuming Service should be declared, or if multiple ones should (see below). +# The service name is mandatory. +onelogin.saml2.sp.attribute_consuming_service.name = My service + +# Attribute Consuming Service description when just one such service should be declared by the SP. +# Ignored if the previous property is commented or empty. +# The service description is optional. +onelogin.saml2.sp.attribute_consuming_service.description = My service description + +# Language used for Attribute Consuming Service name and description when just one such service should be declared by the SP. +# Ignored if the name property is commented or empty. +# The language is optional and default to "en" (English). +onelogin.saml2.sp.attribute_consuming_service.lang = en + +# Attributes to be included in the Attribute Consuming Service when just one such service should be declared by the SP. +# These are indexed properties, starting from 0. The index is used only to enumerate and sort attributes, but it's required. +# The following properties allow to define each attribute: +# - name: mandatory +# - name_format: optional; if omitted, defaults to urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified +# - friendly_name: optional; if omitted, it won't appear in SP metadata +# - required: optional; if omitted or empty, defaults to false +# - value[x]: an attribute value; the [x] is only used only to enumerate and sort values, but it's required +# Please note that only simple values are currently supported and treated internally as strings. Hence no structured values +# and no ability to specify an xsi:type attribute. +# Attribute values are optional and most often they are simply omitted. +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name = Email_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..b64bffd7 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 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 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 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 such service should be declared by the SP. +# These are indexed properties, starting from 0. The index is used only to enumerate and sort attributes, but it's required. +# The following properties allow to define each attribute: +# - name: mandatory +# - name_format: optional; if omitted, defaults to urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified +# - friendly_name: optional; if omitted, it won't appear in SP metadata +# - required: optional; if omitted or empty, defaults to false +# - value[x]: an attribute value; the [x] is only used only to enumerate and sort values, but it's required +# Please note that only simple values are currently supported and treated internally as strings. Hence no structured values +# and no ability to specify an xsi:type attribute. +# Attribute values are optional and most often they are simply omitted. +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name = Email +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name_format = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress +onelogin.saml2.sp.attribute_consuming_service.attribute[0].friendly_name = E-mail "address" +onelogin.saml2.sp.attribute_consuming_service.attribute[0].required = true +onelogin.saml2.sp.attribute_consuming_service.attribute[0].value[0] = foo&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----- @@ -137,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.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.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..db6a0050 --- /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 such service should be declared by the SP. +# These are indexed properties, starting from 0. The index is used only to enumerate and sort attributes, but it's required. +# The following properties allow to define each attribute: +# - name: mandatory +# - name_format: optional; if omitted, defaults to urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified +# - friendly_name: optional; if omitted, it won't appear in SP metadata +# - required: optional; if omitted or empty, defaults to false +# - value[x]: an attribute value; the [x] is only used only to enumerate and sort values, but it's required +# Please note that only simple values are currently supported and treated internally as strings. Hence no structured values +# and no ability to specify an xsi:type attribute. +# Attribute values are optional and most often they are simply omitted. +onelogin.saml2.sp.attribute_consuming_service.attribute[0].name = Email_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 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