[java-plugin-shibd-saml] branch main updated: WIP on ECP with handlers, profile config, machinery for detecting use.
Codeberg
noreply at shibboleth.net
Tue Aug 4 23:30:45 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-plugin-shibd-saml.
View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd-saml/commit/c5d4103d142143bb610e9d02a7d64c1c2a7d35c6
The following commit(s) were added to refs/heads/main by this push:
new c5d4103 WIP on ECP with handlers, profile config, machinery for detecting use.
c5d4103 is described below
commit c5d4103d142143bb610e9d02a7d64c1c2a7d35c6
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue Aug 4 19:30:32 2026 -0400
WIP on ECP with handlers, profile config, machinery for detecting use.
---
.../shibboleth/sp/liberty/paos/package-info.java | 5 +-
.../config/BrowserSSOProfileConfiguration.java | 3 +-
.../profile/config/ECPProfileConfiguration.java | 27 +++++
.../sp/saml/saml2/profile/config/package-info.java | 5 +-
.../META-INF/net.shibboleth.idp/postconfig.xml | 14 ++-
.../net/shibboleth/sp/service/agent/postconfig.xml | 6 +-
.../sp/liberty/paos/impl/package-info.java | 5 +-
.../saml2/binding/decoding/impl}/package-info.java | 4 +-
.../binding/encoding/impl/HTTPPAOSEncoder.java | 42 +++++++
.../saml2/binding/encoding/impl}/package-info.java | 4 +-
.../binding/impl/AddECPRequestHeaderHandler.java | 128 +++++++++++++++++++++
.../binding/impl/AddPAOSRequestHeaderHandler.java | 86 ++++++++++++++
.../impl/ECPHttpServletRequestValidator.java | 95 +++++++++++++++
.../sp/saml/saml2/binding/impl}/package-info.java | 4 +-
.../config/impl/ECPProfileConfiguration.java | 72 +++++++++++-
.../saml2/profile/config/impl/package-info.java | 5 +-
16 files changed, 486 insertions(+), 19 deletions(-)
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java b/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
index 48237e8..168cb11 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
@@ -15,4 +15,7 @@
/**
* PAOS XML APIs.
*/
-package net.shibboleth.sp.liberty.paos;
\ No newline at end of file
+ at NonnullElements
+package net.shibboleth.sp.liberty.paos;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
index a0c9bf2..bd664a9 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
@@ -24,7 +24,6 @@ import javax.annotation.Nullable;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
import net.shibboleth.shared.annotation.ConfigurationSetting;
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
import net.shibboleth.shared.annotation.constraint.NotLive;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.sp.state.StateManager;
@@ -90,7 +89,7 @@ public interface BrowserSSOProfileConfiguration extends SAMLArtifactConsumerProf
* @return list of class references
*/
@ConfigurationSetting(name="authnContextClassRefs")
- @Nonnull @NonnullElements @NotLive @Unmodifiable List<String> getAuthnContextClassRefs(
+ @Nonnull @NotLive @Unmodifiable List<String> getAuthnContextClassRefs(
@Nullable final ProfileRequestContext profileRequestContext);
/**
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/ECPProfileConfiguration.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/ECPProfileConfiguration.java
index 6b921b0..3d96290 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/ECPProfileConfiguration.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/ECPProfileConfiguration.java
@@ -14,8 +14,35 @@
package net.shibboleth.sp.saml.saml2.profile.config;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.saml2.core.IDPList;
+
+import net.shibboleth.shared.annotation.ConfigurationSetting;
+
/** Configuration support for SP SAML 2.0 ECP. */
public interface ECPProfileConfiguration
extends BrowserSSOProfileConfiguration, net.shibboleth.saml.saml2.profile.config.ECPProfileConfiguration {
+ /**
+ * Get the provider name to signal to the ECP client.
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return provider name
+ */
+ @ConfigurationSetting(name="providerName")
+ @Nullable String getProviderName(@Nullable final ProfileRequestContext profileRequestContext);
+
+ /**
+ * Get the {@link IDPList} object to signal to the ECP client.
+ *
+ * @param profileRequestContext current profile request context
+ *
+ * @return the IdP list
+ */
+ @ConfigurationSetting(name="iDPList")
+ @Nullable IDPList getIDPList(@Nullable final ProfileRequestContext profileRequestContext);
+
}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
index e97e075..51a8d7e 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
+++ b/sp-saml-api/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/package-info.java
@@ -15,4 +15,7 @@
/**
* Configuration of SAML support in SP.
*/
-package net.shibboleth.sp.saml.saml2.profile.config;
\ No newline at end of file
+ at NonnullElements
+package net.shibboleth.sp.saml.saml2.profile.config;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 770d5d2..a321b72 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -15,10 +15,16 @@
<!-- Session initiator flows. -->
- <!--
- <bean p:id="sp/initiator/ecp" class="net.shibboleth.sp.profile.BasicSessionInitiatorFlowDescriptor"
- p:activationCondition-ref="" />
- -->
+ <bean p:id="sp/initiator/ecp" class="net.shibboleth.sp.profile.BasicSessionInitiatorFlowDescriptor">
+ <property name="activationCondition">
+ <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+ p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+ <property name="httpServletRequestValidator">
+ <bean class="net.shibboleth.sp.saml.saml2.binding.impl.ECPHttpServletRequestValidator" />
+ </property>
+ </bean>
+ </property>
+ </bean>
<bean p:id="sp/initiator/saml2" class="net.shibboleth.sp.profile.BasicSessionInitiatorFlowDescriptor" />
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
index 1a25896..26178ab 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/sp/service/agent/postconfig.xml
@@ -21,7 +21,7 @@
<bean class="net.shibboleth.sp.saml.saml2.BasicSAML2ProtocolSupportService"
p:order="%{sp.saml.relativeOrder:1}"
p:metadataResolver-ref="shibboleth.MetadataResolverService"
- p:sessionInitiators="saml2"
+ p:sessionInitiators="#{{ 'ecp', 'saml2' }}"
p:logoutInitiators="saml2"
p:tokenConsumers="#{{ 'saml2/post', 'saml2/post-simplesign', 'saml2/artifact' }}"
p:logoutConsumers="#{{ 'saml2/redirect', 'saml2/post', 'saml2/post-simplesign', 'saml2/artifact' }}">
@@ -31,6 +31,7 @@
<property name="defaultProfileConfigurations">
<list>
<ref bean="SAML2.SSO" />
+ <ref bean="SAML2.ECP" />
<ref bean="SAML2.Logout" />
</list>
</property>
@@ -76,7 +77,8 @@
static-field="net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration.FEATURE_FORCEAUTHN"/>
<bean id="SAML2.ECP" parent="SAML2.SSO" lazy-init="true"
- class="net.shibboleth.sp.saml.saml2.profile.config.impl.ECPProfileConfiguration" />
+ class="net.shibboleth.sp.saml.saml2.profile.config.impl.ECPProfileConfiguration"
+ p:providerName="%{sp.saml.providerName:}" />
<bean id="SAML2.Logout" lazy-init="true"
class="net.shibboleth.sp.saml.saml2.profile.config.impl.SingleLogoutProfileConfiguration"
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/liberty/paos/impl/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/liberty/paos/impl/package-info.java
index 268f5e4..6d27561 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/liberty/paos/impl/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/liberty/paos/impl/package-info.java
@@ -15,4 +15,7 @@
/**
* Liberty PAOS XML implementation.
*/
-package net.shibboleth.sp.liberty.paos.impl;
\ No newline at end of file
+ at NonnullElements
+package net.shibboleth.sp.liberty.paos.impl;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/decoding/impl/package-info.java
similarity index 84%
copy from sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
copy to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/decoding/impl/package-info.java
index 48237e8..2e973d5 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/decoding/impl/package-info.java
@@ -13,6 +13,6 @@
*/
/**
- * PAOS XML APIs.
+ * SAML 2.0 MessageDecoders specific to the SP.
*/
-package net.shibboleth.sp.liberty.paos;
\ No newline at end of file
+package net.shibboleth.sp.saml.saml2.binding.decoding.impl;
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/HTTPPAOSEncoder.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/HTTPPAOSEncoder.java
new file mode 100644
index 0000000..6d676e3
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/HTTPPAOSEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.saml.saml2.binding.encoding.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.saml.common.binding.encoding.SAMLMessageEncoder;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.binding.encoding.impl.HTTPSOAP11Encoder;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+
+/**
+ * SAML 2.0 PAOS Encoder.
+ */
+public class HTTPPAOSEncoder extends HTTPSOAP11Encoder
+ implements SAMLMessageEncoder {
+
+ /** Constructor. */
+ public HTTPPAOSEncoder() {
+ setProtocolMessageLoggerSubCategory("SAML");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NotEmpty public String getBindingURI() {
+ return SAMLConstants.SAML2_PAOS_BINDING_URI;
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/package-info.java
similarity index 84%
copy from sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
copy to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/package-info.java
index 48237e8..1881006 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/encoding/impl/package-info.java
@@ -13,6 +13,6 @@
*/
/**
- * PAOS XML APIs.
+ * SAML 2.0 MessageEncoders specific to the SP.
*/
-package net.shibboleth.sp.liberty.paos;
\ No newline at end of file
+package net.shibboleth.sp.saml.saml2.binding.encoding.impl;
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddECPRequestHeaderHandler.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddECPRequestHeaderHandler.java
new file mode 100644
index 0000000..b2e2644
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddECPRequestHeaderHandler.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.saml.saml2.binding.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.core.xml.io.UnmarshallingException;
+import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.IDPList;
+import org.opensaml.saml.saml2.core.Issuer;
+import org.opensaml.saml.saml2.ecp.Request;
+import org.opensaml.soap.messaging.SOAPMessagingSupport;
+import org.opensaml.soap.soap11.ActorBearing;
+import org.opensaml.soap.util.SOAPSupport;
+import org.slf4j.Logger;
+
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.saml.saml2.profile.config.ECPProfileConfiguration;
+
+/**
+ * MessageHandler to add the ECP {@link Request} header to an outgoing PAOS/SOAP envelope.
+ *
+ * <p>The {@link ECPProfileConfiguration} is located via a {@link RelyingPartyContext} located
+ * under the {@link ProfileRequestContext} parent of the {@link MessageContext}.</p>
+ */
+public class AddECPRequestHeaderHandler extends AbstractMessageHandler {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddECPRequestHeaderHandler.class);
+
+ /** The SAML request to copy from. */
+ @NonnullBeforeExec private AuthnRequest authnRequest;
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ if (!super.doPreInvoke(messageContext)) {
+ return false;
+ }
+
+ if (messageContext.getMessage() instanceof AuthnRequest areq) {
+ authnRequest = areq;
+ } else {
+ log.error("{} No AuthnRequest or AssertionConsumerServiceURL available in message context", getLogPrefix());
+ throw new MessageHandlerException("No AuthnRequest available.");
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+ final SAMLObjectBuilder<Request> requestBuilder = (SAMLObjectBuilder<Request>)
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Request>ensureBuilder(
+ Request.DEFAULT_ELEMENT_NAME);
+
+ final Request header = requestBuilder.buildObject(Request.DEFAULT_ELEMENT_NAME);
+
+ // IsPassive in this header block defaults to true if absent (no idea why we did that).
+ final Boolean passive = authnRequest.isPassive();
+ if (passive == null || !passive) {
+ header.setPassive(false);
+ }
+
+ final Issuer issuer = authnRequest.getIssuer();
+ if (issuer != null) {
+ try {
+ header.setIssuer(XMLObjectSupport.cloneXMLObject(issuer));
+ } catch (final MarshallingException | UnmarshallingException e) {
+ throw new MessageHandlerException("Exception cloning Issuer from request", e);
+ }
+ }
+
+ final ProfileRequestContext prc =
+ new RecursiveTypedParentContextLookup<>(ProfileRequestContext.class).apply(messageContext);
+ if (prc != null) {
+ final RelyingPartyContext relyingPartyContext = prc.getSubcontext(RelyingPartyContext.class);
+ if (relyingPartyContext != null &&
+ relyingPartyContext.getProfileConfig() instanceof ECPProfileConfiguration ecp) {
+ header.setProviderName(ecp.getProviderName(prc));
+
+ final IDPList idpList = ecp.getIDPList(prc);
+ if (idpList != null) {
+ try {
+ header.setIDPList(XMLObjectSupport.cloneXMLObject(idpList));
+ } catch (final MarshallingException | UnmarshallingException e) {
+ throw new MessageHandlerException("Exception cloning IDPList from profile configuration", e);
+ }
+ }
+ }
+ }
+
+ SOAPSupport.addSOAP11MustUnderstandAttribute(header, true);
+ SOAPSupport.addSOAP11ActorAttribute(header, ActorBearing.SOAP11_ACTOR_NEXT);
+
+ try {
+ SOAPMessagingSupport.addHeaderBlock(messageContext, header);
+ } catch (final Exception e) {
+ throw new MessageHandlerException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddPAOSRequestHeaderHandler.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddPAOSRequestHeaderHandler.java
new file mode 100644
index 0000000..8071846
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/AddPAOSRequestHeaderHandler.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.saml.saml2.binding.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.core.xml.XMLObjectBuilder;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.soap.messaging.SOAPMessagingSupport;
+import org.opensaml.soap.soap11.ActorBearing;
+import org.opensaml.soap.util.SOAPSupport;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.sp.liberty.paos.Request;
+
+/**
+ * MessageHandler to add the PAOS {@link Request} header to an outgoing PAOS/SOAP envelope.
+ */
+public class AddPAOSRequestHeaderHandler extends AbstractMessageHandler {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AddPAOSRequestHeaderHandler.class);
+
+ /** The SAML request to copy from. */
+ @NonnullBeforeExec private AuthnRequest authnRequest;
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean doPreInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+
+ if (!super.doPreInvoke(messageContext)) {
+ return false;
+ }
+
+ if (messageContext.getMessage() instanceof AuthnRequest areq && areq.getAssertionConsumerServiceURL() != null) {
+ authnRequest = areq;
+ } else {
+ log.error("{} No AuthnRequest or AssertionConsumerServiceURL available in message context", getLogPrefix());
+ throw new MessageHandlerException("No AuthnRequest or AssertionConsumerServiceURL available.");
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+ final XMLObjectBuilder<Request> requestBuilder =
+ XMLObjectProviderRegistrySupport.getBuilderFactory().<Request>ensureBuilder(
+ Request.DEFAULT_ELEMENT_NAME);
+
+ final Request header = requestBuilder.buildObject(Request.DEFAULT_ELEMENT_NAME);
+ header.setService(SAMLConstants.SAML20ECP_NS);
+ header.setMessageID(authnRequest.getID());
+ header.setResponseConsumerURL(authnRequest.getAssertionConsumerServiceURL());
+
+ SOAPSupport.addSOAP11MustUnderstandAttribute(header, true);
+ SOAPSupport.addSOAP11ActorAttribute(header, ActorBearing.SOAP11_ACTOR_NEXT);
+
+ try {
+ SOAPMessagingSupport.addHeaderBlock(messageContext, header);
+ } catch (final Exception e) {
+ throw new MessageHandlerException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/ECPHttpServletRequestValidator.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/ECPHttpServletRequestValidator.java
new file mode 100644
index 0000000..f5972b4
--- /dev/null
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/ECPHttpServletRequestValidator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.saml.saml2.binding.impl;
+
+import java.util.Enumeration;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.springframework.http.MediaType;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.servlet.HttpServletRequestValidator;
+
+/**
+ * A request validator that detects the requirements of an ECP client request:
+ *
+ * <ol>
+ *
+ * <li>Accept header indicating acceptance of the MIME type <pre>application/vnd.paos+xml</pre></li>
+ *
+ * <li>PAOS header specifying the PAOS version with a value, at minimum, of
+ * <pre>urn:liberty:paos:2003-08</pre> and a supported service value of
+ * <pre>urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp</pre></li>
+ *
+ * </ol>
+ */
+public class ECPHttpServletRequestValidator implements HttpServletRequestValidator {
+
+ /** Required start of PAOS header. */
+ @Nonnull private static final String PAOS_VERSION = "ver=\"urn:liberty:paos:2003-08\";";
+
+ /** Media type for PAOS. */
+ @Nonnull private final MediaType paosType;
+
+ /** Constructor. */
+ @SuppressWarnings("null")
+ public ECPHttpServletRequestValidator() {
+ paosType = MediaType.parseMediaType("application/vnd.paos+xml");
+ }
+
+ /** {@inheritDoc} */
+ public void validate(@Nonnull final HttpServletRequest request) throws ServletException {
+
+ boolean sawType = false;
+
+ final Enumeration<String> acceptHeaders = request.getHeaders("Accept");
+ while (!sawType && acceptHeaders.hasMoreElements()) {
+ try {
+ sawType = MediaType.parseMediaTypes(acceptHeaders.nextElement()).stream().anyMatch(paosType::equals);
+ } catch (final Exception e) {
+ throw new ServletException(e);
+ }
+ }
+
+ if (!sawType) {
+ throw new ServletException("Accept header did not contain PAOS media type.");
+ }
+
+ final String paosHeader = request.getHeader("PAOS");
+ if (paosHeader == null) {
+ throw new ServletException("No PAOS header found in request.");
+ }
+
+ if (!paosHeader.startsWith(PAOS_VERSION)) {
+ throw new ServletException("PAOS header does not indicate expected version support.");
+ }
+
+ final String paosService = paosHeader.substring(PAOS_VERSION.length());
+ if (paosService != null) {
+ final String[] options = paosService.split(",");
+ if (options != null && options.length > 0 &&
+ SAMLConstants.SAML20ECP_NS.equals(StringSupport.trimOrNull(options[0]))) {
+ return;
+ }
+ }
+
+ throw new ServletException("PAOS header missing required ECP service indicator.");
+ }
+
+}
\ No newline at end of file
diff --git a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/package-info.java
similarity index 83%
copy from sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
copy to sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/package-info.java
index 48237e8..30e664a 100644
--- a/sp-saml-api/src/main/java/net/shibboleth/sp/liberty/paos/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/binding/impl/package-info.java
@@ -13,6 +13,6 @@
*/
/**
- * PAOS XML APIs.
+ * SAML 2.0 Binding-related MessageHandler implementations.
*/
-package net.shibboleth.sp.liberty.paos;
\ No newline at end of file
+package net.shibboleth.sp.saml.saml2.binding.impl;
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/ECPProfileConfiguration.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/ECPProfileConfiguration.java
index c9f3022..a4b8deb 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/ECPProfileConfiguration.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/ECPProfileConfiguration.java
@@ -14,13 +14,83 @@
package net.shibboleth.sp.saml.saml2.profile.config.impl;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.core.IDPList;
+
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.primitive.StringSupport;
+
/** Configuration support for SP SAML 2.0 ECP. */
public class ECPProfileConfiguration extends BrowserSSOProfileConfiguration
implements net.shibboleth.sp.saml.saml2.profile.config.ECPProfileConfiguration {
+ /** Lookup function to supply provider name. */
+ @Nonnull private Function<ProfileRequestContext,String> providerNameLookupStrategy;
+
+ /** Lookup function to supply IdP list. */
+ @Nonnull private Function<ProfileRequestContext,IDPList> idpListLookupStrategy;
+
/** Constructor. */
public ECPProfileConfiguration() {
- super(net.shibboleth.saml.saml2.profile.config.ECPProfileConfiguration.PROFILE_ID);
+ super(net.shibboleth.saml.saml2.profile.config.ECPProfileConfiguration.PROFILE_ID);
+
+ setResponseBinding(SAMLConstants.SAML2_PAOS_BINDING_URI);
+
+ providerNameLookupStrategy = FunctionSupport.constant(null);
+ idpListLookupStrategy = FunctionSupport.constant(null);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public String getProviderName(@Nullable ProfileRequestContext profileRequestContext) {
+ return providerNameLookupStrategy.apply(profileRequestContext);
+ }
+
+ /**
+ * Set the provider name to signal to the ECP client.
+ *
+ * @param name provider name
+ */
+ public void setProviderName(@Nullable final String name) {
+ providerNameLookupStrategy = FunctionSupport.constant(StringSupport.trimOrNull(name));
+ }
+
+ /**
+ * Set the lookup strategy for the provider name to signal to the ECP client.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setProviderNameLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+ providerNameLookupStrategy = Constraint.isNotNull(strategy, "ProviderName lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public IDPList getIDPList(@Nullable ProfileRequestContext profileRequestContext) {
+ return idpListLookupStrategy.apply(profileRequestContext);
+ }
+
+ /**
+ * Set the {@link IDPList} object to signal to the ECP client.
+ *
+ * @param list IdP list object
+ */
+ public void setIDPList(@Nullable final IDPList list) {
+ idpListLookupStrategy = FunctionSupport.constant(list);
+ }
+
+ /**
+ * Set the lookup strategy for the {@link IDPList} object to signal to the ECP client.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setIDPListLookupStrategy(@Nonnull final Function<ProfileRequestContext,IDPList> strategy) {
+ idpListLookupStrategy = Constraint.isNotNull(strategy, "IDPList lookup strategy cannot be null");
}
}
\ No newline at end of file
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
index 5b08f92..75d1651 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/config/impl/package-info.java
@@ -15,4 +15,7 @@
/**
* SAML 2.0 profile configuration classes.
*/
-package net.shibboleth.sp.saml.saml2.profile.config.impl;
\ No newline at end of file
+ at NonnullElements
+package net.shibboleth.sp.saml.saml2.profile.config.impl;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list