[java-identity-provider] branch master updated: IDP-964 - Support SAML logout propagation via SOAP
Scott Cantor
cantor.2 at osu.edu
Wed Nov 28 11:03:24 EST 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=ba1c5a259b1daea129e2d320a46519d2a3bcdd06
The following commit(s) were added to refs/heads/master by this push:
new ba1c5a2 IDP-964 - Support SAML logout propagation via SOAP
ba1c5a2 is described below
commit ba1c5a259b1daea129e2d320a46519d2a3bcdd06
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Nov 28 11:03:22 2018 -0500
IDP-964 - Support SAML logout propagation via SOAP
https://issues.shibboleth.net/jira/browse/IDP-964
Full SAML SOAP logout with propagation.
Design changes in outbound binding mgmt to accomodate.
---
.../impl/AbstractOutgoingSamlMessageAction.java | 44 ++++++++-
.../src/main/resources/system/conf/utilities.xml | 3 +
.../flows/cas/samlValidate/samlValidate-beans.xml | 18 ++--
.../flows/logout/propagation/saml2-beans.xml | 15 ++-
.../system/flows/logout/propagation/saml2-flow.xml | 7 +-
.../system/flows/saml/saml-abstract-beans.xml | 5 +-
.../flows/saml/saml1/artifact-resolution-beans.xml | 4 +-
.../flows/saml/saml1/attribute-query-beans.xml | 4 +-
.../system/flows/saml/saml1/common-beans.xml | 7 +-
.../flows/saml/saml1/sso-unsolicited-beans.xml | 6 +-
.../flows/saml/saml2/artifact-resolution-beans.xml | 4 +-
.../flows/saml/saml2/attribute-query-beans.xml | 4 +-
.../system/flows/saml/saml2/common-beans.xml | 17 +---
.../system/flows/saml/saml2/idwsf-ssos-beans.xml | 4 +-
.../system/flows/saml/saml2/slo-back-beans.xml | 48 ++++++---
.../system/flows/saml/saml2/slo-back-flow.xml | 69 +++++++++++--
.../flows/saml/saml2/slo-front-abstract-beans.xml | 4 +-
.../system/flows/saml/saml2/sso-artifact-beans.xml | 4 +-
.../system/flows/saml/saml2/sso-ecp-beans.xml | 6 +-
.../system/flows/saml/saml2/sso-post-beans.xml | 4 +-
.../flows/saml/saml2/sso-post-simplesign-beans.xml | 4 +-
.../system/flows/saml/saml2/sso-redirect-beans.xml | 4 +-
.../flows/saml/saml2/sso-unsolicited-beans.xml | 4 +-
.../resources/metadata/example-sp123-metadata.xml | 7 ++
.../InitializeOutboundMessageContextForError.java | 15 ++-
.../impl/PopulateBindingAndEndpointContexts.java | 37 ++++---
.../impl/SpringAwareMessageEncoderFactory.java | 59 +++--------
.../saml/saml2/profile/impl/SOAPLogoutRequest.java | 13 ++-
.../session/impl/PrepareInboundMessageContext.java | 2 +-
...itializeOutboundMessageContextForErrorTest.java | 6 +-
.../PopulateBindingAndEndpointContextsTest.java | 9 +-
.../LogoutContextSessionLookupFunction.java | 53 ++++++++++
.../impl/PopulateLogoutPropagationContext.java | 67 +++++++++----
.../impl/RestoreProfileRequestContextTree.java | 108 +++++++++++++++++++++
.../impl/SaveProfileRequestContextTree.java | 105 ++++++++++++++++++++
.../session/impl/SelectLogoutPropagationFlow.java | 9 +-
36 files changed, 606 insertions(+), 173 deletions(-)
diff --git a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/AbstractOutgoingSamlMessageAction.java b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/AbstractOutgoingSamlMessageAction.java
index 607ff79..78977ae 100644
--- a/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/AbstractOutgoingSamlMessageAction.java
+++ b/idp-cas-impl/src/main/java/net/shibboleth/idp/cas/flow/impl/AbstractOutgoingSamlMessageAction.java
@@ -24,14 +24,20 @@ import net.shibboleth.idp.cas.protocol.ProtocolError;
import net.shibboleth.idp.cas.protocol.TicketValidationRequest;
import net.shibboleth.idp.cas.protocol.TicketValidationResponse;
import net.shibboleth.idp.profile.ActionSupport;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.common.binding.BindingDescriptor;
import org.opensaml.saml.common.messaging.context.SAMLBindingContext;
-import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml1.core.Response;
import org.opensaml.soap.messaging.context.SOAP11Context;
import org.opensaml.soap.soap11.Body;
@@ -49,13 +55,42 @@ public abstract class AbstractOutgoingSamlMessageAction extends
AbstractCASProtocolAction<TicketValidationRequest, TicketValidationResponse> {
/** CAS namespace. */
- protected static final String NAMESPACE = "http://www.ja-sig.org/products/cas/";
+ @Nonnull @NotEmpty protected static final String NAMESPACE = "http://www.ja-sig.org/products/cas/";
/** SOAP envelope needed for old/broken CAS clients. */
+ @Nonnull
private QName envelopeName = new QName(SOAPConstants.SOAP11_NS, Envelope.DEFAULT_ELEMENT_LOCAL_NAME, "SOAP-ENV");
/** SOAP body needed for old/broken CAS clients. */
- private QName bodyName = new QName(SOAPConstants.SOAP11_NS, Body.DEFAULT_ELEMENT_LOCAL_NAME, "SOAP-ENV");
+ @Nonnull private QName bodyName = new QName(SOAPConstants.SOAP11_NS, Body.DEFAULT_ELEMENT_LOCAL_NAME, "SOAP-ENV");
+
+ /** Descriptor for outgoing SOAP binding. */
+ @NonnullAfterInit private BindingDescriptor outgoingBinding;
+
+ /**
+ * Set the {@link BindingDescriptor} describing the outbound binding to use.
+ *
+ * @param descriptor the descriptor
+ *
+ * @since 4.0.0
+ */
+ public void setOutgoingBinding(@Nonnull final BindingDescriptor descriptor) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ outgoingBinding = Constraint.isNotNull(descriptor, "Outgoing BindingDescriptor cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (outgoingBinding == null) {
+ throw new ComponentInitializationException("Outgoing BindingDescriptor cannot be null");
+ }
+ }
+
+
/**
* Build the SAML object.
@@ -84,7 +119,8 @@ public abstract class AbstractOutgoingSamlMessageAction extends
return ProtocolError.IllegalState.event(this);
}
final SAMLBindingContext bindingContext = new SAMLBindingContext();
- bindingContext.setBindingUri(SAMLConstants.SAML1_SOAP11_BINDING_URI);
+ bindingContext.setBindingUri(outgoingBinding.getId());
+ bindingContext.setBindingDescriptor(outgoingBinding);
msgContext.addSubcontext(bindingContext);
// Ensure message uses SOAP-ENV ns prefix required by old/broken CAS clients
diff --git a/idp-conf/src/main/resources/system/conf/utilities.xml b/idp-conf/src/main/resources/system/conf/utilities.xml
index f95daea..e15e525 100644
--- a/idp-conf/src/main/resources/system/conf/utilities.xml
+++ b/idp-conf/src/main/resources/system/conf/utilities.xml
@@ -140,6 +140,9 @@
<bean id="shibboleth.ChildLookup.SessionContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
c:type="#{ T(net.shibboleth.idp.session.context.SessionContext) }" />
+ <bean id="shibboleth.ChildLookup.LogoutContext"
+ class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+ c:type="#{ T(net.shibboleth.idp.session.context.LogoutContext) }" />
<bean id="shibboleth.ChildLookup.SAMLBindingContext"
class="org.opensaml.messaging.context.navigate.ChildContextLookup"
diff --git a/idp-conf/src/main/resources/system/flows/cas/samlValidate/samlValidate-beans.xml b/idp-conf/src/main/resources/system/flows/cas/samlValidate/samlValidate-beans.xml
index b23d03c..5caa20b 100644
--- a/idp-conf/src/main/resources/system/flows/cas/samlValidate/samlValidate-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/cas/samlValidate/samlValidate-beans.xml
@@ -13,25 +13,29 @@
scope="prototype"
c:messageDecoder-ref="SoapDecoder" />
- <bean id="SoapDecoder"
+ <bean id="SoapDecoder"
class="org.opensaml.saml.saml1.binding.decoding.impl.HTTPSOAP11Decoder"
scope="prototype"
p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
<bean id="ProcessSamlMessage"
- class="net.shibboleth.idp.cas.flow.impl.ProcessSamlMessageAction" />
+ class="net.shibboleth.idp.cas.flow.impl.ProcessSamlMessageAction"
+ scope="prototype" />
- <bean id="BuildSamlValidationSuccessMessage"
+ <bean id="BuildSamlValidationSuccessMessage"
class="net.shibboleth.idp.cas.flow.impl.BuildSamlValidationSuccessMessageAction"
+ scope="prototype"
c:strategy-ref="shibboleth.DefaultIdentifierGenerationStrategy"
- c:id="%{idp.entityID}" />
+ c:id="%{idp.entityID}"
+ p:outgoingBinding-ref="shibboleth.Binding.SAML1SOAP" />
<bean id="BuildSamlValidationFailureMessage"
- class="net.shibboleth.idp.cas.flow.impl.BuildSamlValidationFailureMessageAction" />
+ class="net.shibboleth.idp.cas.flow.impl.BuildSamlValidationFailureMessageAction"
+ scope="prototype"
+ p:outgoingBinding-ref="shibboleth.Binding.SAML1SOAP" />
<bean id="MessageEncoderFactory"
- class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageEncoderFactory"
- p:bindings-ref="shibboleth.OutgoingSOAPBindings" />
+ class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageEncoderFactory" />
<bean id="EncodeMessage"
class="org.opensaml.profile.action.impl.EncodeMessage"
diff --git a/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-beans.xml b/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-beans.xml
index bc8bcc6..192691b 100644
--- a/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-beans.xml
@@ -31,7 +31,20 @@
<util:constant id="shibboleth.MetadataLookup.Role"
static-field="org.opensaml.saml.saml2.metadata.SPSSODescriptor.DEFAULT_ELEMENT_NAME" />
- <alias name="shibboleth.OutgoingSAML2SLOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.ext.spring.util.SpringExpressionFunction"
+ c:expression="#custom[#input.isBrowserProfile() ? 'full' : 'soap']">
+ <property name="customObject">
+ <map>
+ <entry key="full">
+ <ref bean="shibboleth.OutgoingSAML2SLOBindings" />
+ </entry>
+ <entry key="soap">
+ <ref bean="shibboleth.OutgoingSOAPBindings" />
+ </entry>
+ </map>
+ </property>
+ </bean>
<bean id="PrepareInboundMessageContext"
class="net.shibboleth.idp.saml.session.impl.PrepareInboundMessageContext" scope="prototype" />
diff --git a/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-flow.xml b/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-flow.xml
index 1a2294a..56cf2af 100644
--- a/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/logout/propagation/saml2-flow.xml
@@ -101,16 +101,19 @@
</end-state>
<!--
- Terminal state. This is "proceed" because the parent flow will respond to that
+ Terminal states. This is usually "proceed" because the parent flow will respond to that
by emitting the outcome in the LogoutPropagationContext, which is set appropriately.
-->
<end-state id="proceed"/>
+ <end-state id="EndpointResolutionFailed" />
<!--
- Default is to turn non-proceed events into an error, which in this special flow is actually "proceed".
+ Catch-all is to turn non-proceed events into an error, which in this special flow is actually "proceed".
+ Some specifically interesting events are captured.
-->
<global-transitions>
<transition on-exception="java.lang.RuntimeException" to="LogRuntimeException" />
+ <transition on="EndpointResolutionFailed" to="EndpointResolutionFailed" />
<transition on="#{!'proceed'.equals(currentEvent.id)}" to="proceed" />
</global-transitions>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
index 47d70c6..c3f4d92 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml-abstract-beans.xml
@@ -124,7 +124,7 @@
class="net.shibboleth.idp.saml.profile.impl.PopulateBindingAndEndpointContexts" scope="prototype"
p:endpointResolver-ref="shibboleth.EndpointResolver"
p:endpointType="#{getObject('shibboleth.EndpointType')}"
- p:bindings-ref="shibboleth.OutgoingBindings"
+ p:bindingDescriptorsLookupStrategy-ref="shibboleth.OutgoingBindingsLookupStrategy"
p:artifactImpliesSecureChannel="%{idp.artifact.secureChannel:true}" />
<bean id="PopulateDelegationContext"
@@ -321,8 +321,7 @@
</property>
</bean>
- <bean id="messageEncoderFactory" class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageEncoderFactory"
- p:bindings-ref="shibboleth.OutgoingBindings" />
+ <bean id="messageEncoderFactory" class="net.shibboleth.idp.saml.profile.impl.SpringAwareMessageEncoderFactory" />
<bean id="EncodeMessage" class="org.opensaml.profile.action.impl.EncodeMessage" scope="prototype"
p:messageEncoderFactory-ref="messageEncoderFactory"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/artifact-resolution-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/artifact-resolution-beans.xml
index ee4473a..e1768ba 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/artifact-resolution-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/artifact-resolution-beans.xml
@@ -36,7 +36,9 @@
p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:defaultPortInsecure="%{idp.security.defaultPortInsecure:true}" />
- <alias name="shibboleth.OutgoingSOAPBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSOAPBindings" />
<bean id="ResolveArtifacts"
class="org.opensaml.saml.saml1.profile.impl.ResolveArtifacts" scope="prototype"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
index f2d2ee7..1ec5849 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/attribute-query-beans.xml
@@ -37,7 +37,9 @@
p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:defaultPortInsecure="%{idp.security.defaultPortInsecure:true}" />
- <alias name="shibboleth.OutgoingSOAPBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSOAPBindings" />
<bean id="CopyNameIdentifierFromRequest"
class="org.opensaml.saml.saml1.profile.impl.CopyNameIdentifierFromRequest" scope="prototype" />
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
index e53c4ff..763fcca 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/common-beans.xml
@@ -60,11 +60,8 @@
</bean>
<bean id="InitializeOutboundMessageContextForError"
- class="net.shibboleth.idp.saml.profile.impl.InitializeOutboundMessageContextForError" scope="prototype">
- <property name="outboundBinding">
- <util:constant static-field="org.opensaml.saml.common.xml.SAMLConstants.SAML1_SOAP11_BINDING_URI"/>
- </property>
- </bean>
+ class="net.shibboleth.idp.saml.profile.impl.InitializeOutboundMessageContextForError" scope="prototype"
+ p:outboundBinding-ref="shibboleth.Binding.SAML1SOAP" />
<bean id="AddStatusToResponse"
class="org.opensaml.saml.saml1.profile.impl.AddStatusToResponse" scope="prototype"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml1/sso-unsolicited-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml1/sso-unsolicited-beans.xml
index 32135ec..b605d4d 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml1/sso-unsolicited-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml1/sso-unsolicited-beans.xml
@@ -20,7 +20,9 @@
p:bindingDescriptor-ref="shibboleth.Binding.LegacyShibboleth" />
</constructor-arg>
</bean>
-
- <alias name="shibboleth.OutgoingSAML1SSOBindings" alias="shibboleth.OutgoingBindings"/>
+
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML1SSOBindings" />
</beans>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/artifact-resolution-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/artifact-resolution-beans.xml
index 7b2b339..05e45f1 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/artifact-resolution-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/artifact-resolution-beans.xml
@@ -36,7 +36,9 @@
p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:defaultPortInsecure="%{idp.security.defaultPortInsecure:true}" />
- <alias name="shibboleth.OutgoingSOAPBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSOAPBindings" />
<bean id="AddArtifactResponseShell" class="org.opensaml.saml.saml2.profile.impl.AddStatusResponseShell" scope="prototype"
p:issuerLookupStrategy-ref="shibboleth.ResponderIdLookup.Simple"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
index 4d80acb..95779b9 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/attribute-query-beans.xml
@@ -37,7 +37,9 @@
p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:defaultPortInsecure="%{idp.security.defaultPortInsecure:true}" />
- <alias name="shibboleth.OutgoingSOAPBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSOAPBindings" />
<bean id="CopyNameIDFromRequest"
class="org.opensaml.saml.saml2.profile.impl.CopyNameIDFromRequest" scope="prototype" />
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
index 8caa745..1c212ef 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/common-beans.xml
@@ -74,11 +74,8 @@
</bean>
<bean id="InitializeOutboundMessageContextForError"
- class="net.shibboleth.idp.saml.profile.impl.InitializeOutboundMessageContextForError" scope="prototype">
- <property name="outboundBinding">
- <util:constant static-field="org.opensaml.saml.common.xml.SAMLConstants.SAML2_SOAP11_BINDING_URI"/>
- </property>
- </bean>
+ class="net.shibboleth.idp.saml.profile.impl.InitializeOutboundMessageContextForError" scope="prototype"
+ p:outboundBinding-ref="shibboleth.Binding.SAML2SOAP" />
<bean id="AddStatusToResponse"
class="org.opensaml.saml.saml2.profile.impl.AddStatusToResponse" scope="prototype"
@@ -99,16 +96,6 @@
<bean class="net.shibboleth.idp.profile.context.navigate.SpringStatusMessageLookupFunction" />
</property>
</bean>
-
- <bean id="AddPartialLogoutStatusToResponse"
- class="org.opensaml.saml.saml2.profile.impl.AddStatusToResponse" scope="prototype">
- <property name="statusCodes">
- <list>
- <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.SUCCESS" />
- <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.PARTIAL_LOGOUT" />
- </list>
- </property>
- </bean>
<bean id="EncryptToSelfParametersStrategy" class="org.opensaml.saml.security.impl.InlineSelfEncryptionParametersStrategy">
<constructor-arg>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/idwsf-ssos-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/idwsf-ssos-beans.xml
index f4ce453..3bc20a8 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/idwsf-ssos-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/idwsf-ssos-beans.xml
@@ -28,7 +28,9 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingIDWSFSSOSBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingIDWSFSSOSBindings" />
<bean id="shibboleth.BindingSpecificMessageHandler"
class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain" scope="prototype">
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-beans.xml
index f7e2e3e..90fa64d 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-beans.xml
@@ -23,6 +23,7 @@
<bean id="InitializeProfileRequestContext"
class="net.shibboleth.idp.profile.impl.InitializeProfileRequestContext" scope="prototype"
p:profileId="#{T(net.shibboleth.idp.saml.saml2.profile.config.SingleLogoutProfileConfiguration).PROFILE_ID}"
+ p:browserProfile="false"
p:loggingId="%{idp.service.logging.saml2slo:Logout}" />
<bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
@@ -34,28 +35,44 @@
</constructor-arg>
</bean>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSOAPBindings" />
+
<bean id="InitializeMessageChannelSecurityContext"
class="org.opensaml.profile.action.impl.HttpServletRequestMessageChannelSecurity" scope="prototype"
p:httpServletRequest-ref="shibboleth.HttpServletRequest"
p:defaultPortInsecure="%{idp.security.defaultPortInsecure:true}" />
- <alias name="shibboleth.OutgoingSOAPBindings" alias="shibboleth.OutgoingBindings"/>
-
<bean id="ProcessLogoutRequest"
class="net.shibboleth.idp.saml.saml2.profile.impl.ProcessLogoutRequest" scope="prototype"
p:sessionManager-ref="shibboleth.SessionManager"
p:sessionResolver-ref="shibboleth.SessionManager" />
<bean id="LogoutPopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
- p:fieldExtractors="#{getObject('shibboleth.LogoutAuditExtractors') ?: getObject('shibboleth.DefaultLogoutAuditExtractors')}" />
+ p:fieldExtractors="#{getObject('shibboleth.LogoutRequestAuditExtractors') ?: getObject('shibboleth.DefaultLogoutRequestAuditExtractors')}" />
+
+ <bean id="SaveProfileRequestContextTree"
+ class="net.shibboleth.idp.session.impl.SaveProfileRequestContextTree" scope="prototype" />
+
+ <bean id="RestoreProfileRequestContextTree"
+ class="net.shibboleth.idp.session.impl.RestoreProfileRequestContextTree" scope="prototype" />
+
+ <bean id="PopulateLogoutPropagationContext"
+ class="net.shibboleth.idp.session.impl.PopulateLogoutPropagationContext" scope="prototype">
+ <property name="sessionLookupStrategy">
+ <bean class="com.google.common.base.Functions" factory-method="compose"
+ c:f-ref="shibboleth.ChildLookup.LogoutContext">
+ <constructor-arg name="g">
+ <bean class="net.shibboleth.idp.session.context.navigate.LogoutContextSessionLookupFunction" />
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
- <!--
- TODO: will be used when logout propagation is enabled
- <bean id="PopulateMultiRPContextFromLogoutContext"
- class="net.shibboleth.idp.session.impl.PopulateMultiRPContextFromLogoutContext" scope="prototype"
- p:roleDescriptorResolver-ref="shibboleth.RoleDescriptorResolver"
- p:sessionTypeProtocolMap="#{getObject('shibboleth.SessionTypeProtocolMap') ?: getObject('shibboleth.DefaultSessionTypeProtocolMap')}" />
- -->
+ <bean id="SelectPropagationFlow"
+ class="net.shibboleth.idp.session.impl.SelectLogoutPropagationFlow" scope="prototype"
+ c:selector-ref="shibboleth.LogoutPropagationFlowSelector" />
<bean id="AddLogoutResponse" class="org.opensaml.saml.saml2.profile.impl.AddStatusResponseShell" scope="prototype"
p:overwriteExisting="true"
@@ -67,11 +84,20 @@
</property>
</bean>
+ <bean id="AddPartialLogoutStatusToResponse"
+ class="org.opensaml.saml.saml2.profile.impl.AddStatusToResponse" scope="prototype">
+ <property name="statusCodes">
+ <list>
+ <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.SUCCESS" />
+ <util:constant static-field="org.opensaml.saml.saml2.core.StatusCode.PARTIAL_LOGOUT" />
+ </list>
+ </property>
+ </bean>
+
<bean id="shibboleth.PreEncodeMessageHandler"
class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain" scope="prototype">
<property name="handlers">
<util:list>
- <!-- TODO outbound handlers -->
<bean class="org.opensaml.saml.common.binding.security.impl.SAMLOutboundProtocolMessageSigningHandler"
scope="prototype" p:signErrorResponses="%{idp.errors.signed:true}" />
</util:list>
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-flow.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-flow.xml
index 9356e19..f30f92c 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-back-flow.xml
@@ -5,6 +5,10 @@
<!-- Bootstrap action has to be at the "top" of the flow after this flow prepends itself to parent. -->
+ <on-start>
+ <set name="conversationScope.LogoutStatus" value="true" />
+ </on-start>
+
<action-state id="InitializeProfileRequestContext">
<evaluate expression="InitializeProfileRequestContext" />
<evaluate expression="'proceed'" />
@@ -15,16 +19,66 @@
<action-state id="DoProfileWork">
<evaluate expression="ProcessLogoutRequest" />
<evaluate expression="LogoutPopulateAuditContext" />
- <!--
- TODO: will be used when logout propagation is enabled
- <evaluate expression="PopulateMultiRPContextFromLogoutContext" />
- -->
<evaluate expression="'proceed'" />
- <transition on="proceed" to="CheckAsync" />
+ <transition on="proceed" to="CheckForSessions" />
+ </action-state>
+
+ <decision-state id="CheckForSessions">
+ <if test="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.LogoutContext), true).getSessionMap().isEmpty()"
+ then="CheckAsync"
+ else="SaveProfileRequestContextTree" />
+ </decision-state>
+
+ <action-state id="SaveProfileRequestContextTree">
+ <evaluate expression="SaveProfileRequestContextTree" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="PopulateNextLogoutPropagationContext" />
+ </action-state>
+
+ <action-state id="PopulateNextLogoutPropagationContext">
+ <evaluate expression="PopulateLogoutPropagationContext" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="PropagateLogout" />
+ <transition on="SessionNotFound" to="RestoreProfileRequestContextTree" />
+ </action-state>
+
+ <action-state id="PropagateLogout">
+ <evaluate expression="SelectPropagationFlow" />
+
+ <!-- Call a subflow with the same ID as the event. -->
+ <transition on="#{currentEvent.id.startsWith('logoutprop/')}" to="CallPropagationFlow" />
+
+ <!-- Try the next RP's session(s), and record overall failure. -->
+ <transition to="PopulateNextLogoutPropagationContext">
+ <set name="conversationScope.LogoutStatus" value="false" />
+ </transition>
</action-state>
+ <!-- This invokes a propagation flow. We check for failure but otherwise just keep iterating. -->
+ <subflow-state id="CallPropagationFlow" subflow="#{currentEvent.id}">
+ <input name="calledAsSubflow" value="true" />
+ <transition to="PopulateNextLogoutPropagationContext" />
+ <on-exit>
+ <evaluate expression="WriteAuditLog" />
+ <!-- If overall status remains positive, intersect with the last propagation result's success. -->
+ <evaluate expression="conversationScope.LogoutStatus and opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.LogoutPropagationContext)).getResult() == T(net.shibboleth.idp.session.context.LogoutPropagationContext.Result).Success"
+ result="conversationScope.LogoutStatus" />
+ <evaluate expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.session.context.LogoutPropagationContext))" />
+ <evaluate expression="opensamlProfileRequestContext.removeSubcontext(T(net.shibboleth.idp.profile.context.AuditContext))" />
+ </on-exit>
+ </subflow-state>
+
+ <action-state id="RestoreProfileRequestContextTree">
+ <evaluate expression="RestoreProfileRequestContextTree" />
+ <evaluate expression="'proceed'" />
+
+ <transition on="proceed" to="CheckAsync" />
+ </action-state>
+
<decision-state id="CheckAsync">
<if test="opensamlProfileRequestContext.getInboundMessageContext().getMessage().getExtensions() == null or opensamlProfileRequestContext.getInboundMessageContext().getMessage().getExtensions().getUnknownXMLObjects(T(org.opensaml.saml.ext.saml2aslo.Asynchronous).DEFAULT_ELEMENT_NAME).isEmpty()"
then="BuildResponse" else="DoAsync" />
@@ -57,10 +111,8 @@
<transition on="proceed" to="CheckForPartialLogout" />
</action-state>
- <!-- TODO: this partial logout machinery will change once propagation exists -->
-
<decision-state id="CheckForPartialLogout">
- <if test="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.LogoutContext), true).getSessionMap().isEmpty()"
+ <if test="conversationScope.LogoutStatus"
then="CompleteResponse" else="PartialLogout" />
</decision-state>
@@ -86,6 +138,7 @@
<on-entry>
<evaluate expression="LogEvent" />
</on-entry>
+ <evaluate expression="RestoreProfileRequestContextTree" />
<evaluate expression="InitializeOutboundMessageContextForError" />
<evaluate expression="AddLogoutResponse" />
<evaluate expression="AddInResponseToToResponse" />
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-front-abstract-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-front-abstract-beans.xml
index e789a2d..2c1a453 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/slo-front-abstract-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/slo-front-abstract-beans.xml
@@ -30,7 +30,9 @@
class="org.opensaml.profile.action.impl.StaticMessageChannelSecurity" scope="prototype"
p:confidentialityActive="false" p:integrityActive="false" />
- <alias name="shibboleth.OutgoingSAML2SLOFrontBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SLOFrontBindings" />
<bean id="ProcessLogoutRequest"
class="net.shibboleth.idp.saml.saml2.profile.impl.ProcessLogoutRequest" scope="prototype"
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-artifact-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-artifact-beans.xml
index 74443d5..74d2529 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-artifact-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-artifact-beans.xml
@@ -37,6 +37,8 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingSAML2SSOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SSOBindings" />
</beans>
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-ecp-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-ecp-beans.xml
index 85f2264..96651d4 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-ecp-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-ecp-beans.xml
@@ -27,8 +27,10 @@
p:bindingDescriptor-ref="shibboleth.Binding.SAML2SOAP" />
</constructor-arg>
</bean>
-
- <alias name="shibboleth.OutgoingECPBindings" alias="shibboleth.OutgoingBindings"/>
+
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingECPBindings" />
<bean id="shibboleth.BindingSpecificMessageHandler"
class="org.opensaml.messaging.handler.impl.BasicMessageHandlerChain" scope="prototype">
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-beans.xml
index 186f681..4668288 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-beans.xml
@@ -28,6 +28,8 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingSAML2SSOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SSOBindings" />
</beans>
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-simplesign-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-simplesign-beans.xml
index 041675f..a964102 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-simplesign-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-post-simplesign-beans.xml
@@ -29,6 +29,8 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingSAML2SSOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SSOBindings" />
</beans>
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-redirect-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-redirect-beans.xml
index 7a75dc1..7d3988e 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-redirect-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-redirect-beans.xml
@@ -28,6 +28,8 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingSAML2SSOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SSOBindings" />
</beans>
\ No newline at end of file
diff --git a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-unsolicited-beans.xml b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-unsolicited-beans.xml
index d618431..5328828 100644
--- a/idp-conf/src/main/resources/system/flows/saml/saml2/sso-unsolicited-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/saml/saml2/sso-unsolicited-beans.xml
@@ -27,6 +27,8 @@
</constructor-arg>
</bean>
- <alias name="shibboleth.OutgoingSAML2SSOBindings" alias="shibboleth.OutgoingBindings"/>
+ <bean id="shibboleth.OutgoingBindingsLookupStrategy"
+ class="net.shibboleth.utilities.java.support.logic.FunctionSupport" factory-method="constant"
+ c:_0-ref="shibboleth.OutgoingSAML2SSOBindings" />
</beans>
\ No newline at end of file
diff --git a/idp-conf/src/test/resources/metadata/example-sp123-metadata.xml b/idp-conf/src/test/resources/metadata/example-sp123-metadata.xml
index d0f6b64..b2e75b2 100644
--- a/idp-conf/src/test/resources/metadata/example-sp123-metadata.xml
+++ b/idp-conf/src/test/resources/metadata/example-sp123-metadata.xml
@@ -23,6 +23,10 @@ Example metadata for mock SPs with entityIDs of form 'https://sp[123].example.or
<saml:AttributeValue>http://refeds.org/category/research-and-scholarship
</saml:AttributeValue>
</saml:Attribute>
+ <saml:Attribute Name="http://shibboleth.net/ns/profiles/saml2/logout/signSOAPRequests"
+ NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+ <saml:AttributeValue>true</saml:AttributeValue>
+ </saml:Attribute>
</mdattr:EntityAttributes>
</Extensions>
<SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol" AuthnRequestsSigned="false">
@@ -91,6 +95,7 @@ h+XNZ5oAql/cjrwDWw==
</KeyInfo>
</KeyDescriptor>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://localhost:8443/sp/sp1/SAML2/SOAP/SLO"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:8443/sp/sp1/SAML2/Redirect/SLO"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost/sp/sp1/SAML2/POST/ACS" index="1"/>
@@ -202,6 +207,7 @@ h+XNZ5oAql/cjrwDWw==
</KeyInfo>
</KeyDescriptor>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://localhost:8443/sp/sp2/SAML2/SOAP/SLO"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:8443/sp/sp2/SAML2/Redirect/SLO"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost/sp/sp2/SAML2/POST/ACS" index="1"/>
@@ -313,6 +319,7 @@ h+XNZ5oAql/cjrwDWw==
</KeyInfo>
</KeyDescriptor>
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://localhost:8443/sp/sp3/SAML2/SOAP/SLO"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:8443/sp/sp3/SAML2/Redirect/SLO"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost/sp/sp3/SAML2/POST/ACS" index="1"/>
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForError.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForError.java
index d67eb42..681e060 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForError.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForError.java
@@ -27,7 +27,6 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.primitive.StringSupport;
import org.opensaml.messaging.context.BaseContext;
import org.opensaml.messaging.context.MessageContext;
@@ -36,6 +35,7 @@ import org.opensaml.profile.action.ActionSupport;
import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
+import org.opensaml.saml.common.binding.BindingDescriptor;
import org.opensaml.saml.common.binding.SAMLBindingSupport;
import org.opensaml.saml.common.messaging.context.SAMLBindingContext;
import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
@@ -73,7 +73,7 @@ public class InitializeOutboundMessageContextForError extends AbstractProfileAct
@Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
/** Outbound binding to use. */
- @NonnullAfterInit private String outboundBinding;
+ @NonnullAfterInit private BindingDescriptor outboundBinding;
/** The {@link SAMLPeerEntityContext} to base the outbound context on, if any. */
@Nullable private SAMLPeerEntityContext peerEntityCtx;
@@ -115,11 +115,10 @@ public class InitializeOutboundMessageContextForError extends AbstractProfileAct
/**
* Set the outbound binding to use for the error response.
*
- * @param binding binding identifier
+ * @param binding binding descriptor
*/
- public void setOutboundBinding(@Nonnull @NotEmpty final String binding) {
- outboundBinding = Constraint.isNotNull(StringSupport.trimOrNull(binding),
- "Outbound binding URI cannot be null or empty");
+ public void setOutboundBinding(@Nonnull @NotEmpty final BindingDescriptor binding) {
+ outboundBinding = Constraint.isNotNull(binding, "Outbound BindingDescriptor cannot be null or empty");
}
/** {@inheritDoc} */
@@ -128,7 +127,7 @@ public class InitializeOutboundMessageContextForError extends AbstractProfileAct
super.doInitialize();
if (outboundBinding == null) {
- throw new ComponentInitializationException("Outbound binding URI cannot be null");
+ throw new ComponentInitializationException("Outbound BindingDescriptor cannot be null");
}
}
@@ -172,7 +171,7 @@ public class InitializeOutboundMessageContextForError extends AbstractProfileAct
bindingCtx.setRelayState(SAMLBindingSupport.getRelayState(
profileRequestContext.getInboundMessageContext()));
}
- bindingCtx.setBindingUri(outboundBinding);
+ bindingCtx.setBindingDescriptor(outboundBinding);
// Copy SAML peer context and metadata if it exists.
if (peerEntityCtx != null) {
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContexts.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContexts.java
index 1d6ca7b..9e67888 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContexts.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContexts.java
@@ -37,6 +37,7 @@ import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
@@ -70,8 +71,6 @@ import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
/**
@@ -107,9 +106,9 @@ public class PopulateBindingAndEndpointContexts extends AbstractProfileAction {
/** Endpoint resolver. */
@NonnullAfterInit private EndpointResolver<?> endpointResolver;
- /** List of possible bindings, in preference order. */
- @Nonnull @NonnullElements private List<BindingDescriptor> bindingDescriptors;
-
+ /** Lookup strategy for bindings. */
+ @Nonnull private Function<ProfileRequestContext,List<BindingDescriptor>> bindingDescriptorsLookupStrategy;
+
/** Strategy function for access to {@link RelyingPartyContext}. */
@Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
@@ -125,6 +124,9 @@ public class PopulateBindingAndEndpointContexts extends AbstractProfileAction {
/** Strategy function for access to {@link SAMLArtifactContext} to populate. */
@Nonnull private Function<ProfileRequestContext,SAMLArtifactContext> artifactContextLookupStrategy;
+ /** List of possible bindings, in preference order. */
+ @Nonnull @NonnullElements private List<BindingDescriptor> bindingDescriptors;
+
/** Whether an artifact-based binding implies the use of a secure channel. */
private boolean artifactImpliesSecureChannel;
@@ -151,7 +153,9 @@ public class PopulateBindingAndEndpointContexts extends AbstractProfileAction {
/** Constructor. */
public PopulateBindingAndEndpointContexts() {
- bindingDescriptors = Collections.emptyList();
+ bindingDescriptorsLookupStrategy =
+ FunctionSupport.<ProfileRequestContext,List<BindingDescriptor>>constant(
+ Collections.<BindingDescriptor>emptyList());
relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
@@ -201,17 +205,20 @@ public class PopulateBindingAndEndpointContexts extends AbstractProfileAction {
}
/**
- * Set the bindings to evaluate for use, in preference order.
+ * Set lookup strategy to return the bindings to evaluate for use, in preference order.
+ *
+ * @param strategy lookup strategy
*
- * @param bindings bindings to consider
+ * @since 4.0.0
*/
- public void setBindings(@Nonnull @NonnullElements final List<BindingDescriptor> bindings) {
+ public void setBindingDescriptorsLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,List<BindingDescriptor>> strategy) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- Constraint.isNotNull(bindings, "Binding descriptor list cannot be null");
- bindingDescriptors = new ArrayList<>(Collections2.filter(bindings, Predicates.notNull()));
+ bindingDescriptorsLookupStrategy =
+ Constraint.isNotNull(strategy, "Binding descriptors lookup strategy cannot be null");
}
-
+
/**
* Set lookup strategy for {@link RelyingPartyContext}.
*
@@ -355,8 +362,14 @@ public class PopulateBindingAndEndpointContexts extends AbstractProfileAction {
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
return false;
}
+
+ bindingDescriptors = bindingDescriptorsLookupStrategy.apply(profileRequestContext);
+ if (bindingDescriptors == null) {
+ bindingDescriptors = Collections.emptyList();
+ }
mdContext = metadataContextLookupStrategy.apply(profileRequestContext);
+
return true;
}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/SpringAwareMessageEncoderFactory.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/SpringAwareMessageEncoderFactory.java
index 4f03706..8282a53 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/SpringAwareMessageEncoderFactory.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/profile/impl/SpringAwareMessageEncoderFactory.java
@@ -17,18 +17,14 @@
package net.shibboleth.idp.saml.profile.impl;
-import java.util.List;
-
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.shibboleth.idp.profile.context.SpringRequestContext;
import net.shibboleth.idp.saml.binding.BindingDescriptor;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.ThreadSafeAfterInit;
import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
import org.opensaml.messaging.encoder.MessageEncoder;
import org.opensaml.profile.action.MessageEncoderFactory;
@@ -38,12 +34,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
/**
- * A factory that returns the correct {@link MessageEncoder} to use based on an input collection of
- * descriptors that map to a Spring bean ID.
+ * A factory that returns the correct {@link MessageEncoder} to use based on an underlying {@link BindingDescriptor}.
*/
@ThreadSafeAfterInit
public class SpringAwareMessageEncoderFactory extends AbstractInitializableComponent implements MessageEncoderFactory {
@@ -51,31 +44,6 @@ public class SpringAwareMessageEncoderFactory extends AbstractInitializableCompo
/** Class logger. */
@Nonnull private final Logger log = LoggerFactory.getLogger(SpringAwareMessageEncoderFactory.class);
- /** Map of bindings to descriptors. */
- @Nonnull @NonnullElements private ListMultimap<String,BindingDescriptor> bindingMap;
-
- /** Constructor. */
- public SpringAwareMessageEncoderFactory() {
- bindingMap = ArrayListMultimap.create();
- }
-
- /**
- * Set the bindings to evaluate for use, in preference order.
- *
- * @param bindings bindings to consider
- */
- public void setBindings(@Nonnull @NonnullElements final List<BindingDescriptor> bindings) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- Constraint.isNotNull(bindings, "Binding descriptor list cannot be null");
-
- bindingMap.clear();
- for (final BindingDescriptor binding : bindings) {
- if (binding != null && binding.getId() != null) {
- bindingMap.put(binding.getId(), binding);
- }
- }
- }
-
/** {@inheritDoc} */
@Override
@Nullable public MessageEncoder getMessageEncoder(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -83,7 +51,7 @@ public class SpringAwareMessageEncoderFactory extends AbstractInitializableCompo
final SpringRequestContext springContext = profileRequestContext.getSubcontext(SpringRequestContext.class);
if (springContext == null || springContext.getRequestContext() == null) {
- log.warn("No outbound message context, unable to lookup message encoder");
+ log.warn("No Spring request context, unable to lookup message encoder bean");
return null;
} else if (profileRequestContext.getOutboundMessageContext() == null) {
log.warn("No outbound message context, unable to lookup message encoder");
@@ -92,22 +60,21 @@ public class SpringAwareMessageEncoderFactory extends AbstractInitializableCompo
final SAMLBindingContext bindingContext =
profileRequestContext.getOutboundMessageContext().getSubcontext(SAMLBindingContext.class);
- if (bindingContext == null || bindingContext.getBindingUri() == null) {
- log.warn("Binding URI was not available, unable to lookup message encoder");
+ if (bindingContext == null || bindingContext.getBindingDescriptor() == null
+ || !(bindingContext.getBindingDescriptor() instanceof BindingDescriptor)) {
+ log.warn("BindingDescriptor was not available, unable to lookup message encoder");
return null;
}
log.debug("Looking up message encoder based on binding URI: {}", bindingContext.getBindingUri());
-
- final List<BindingDescriptor> bindings = bindingMap.get(bindingContext.getBindingUri());
- for (final BindingDescriptor binding : bindings) {
- if (binding.getEncoderBeanId() != null) {
- try {
- return springContext.getRequestContext().getActiveFlow().getApplicationContext().getBean(
- binding.getEncoderBeanId(), MessageEncoder.class);
- } catch (final BeansException e) {
- log.warn("Error instantiating message encoder from bean ID {}", binding.getEncoderBeanId(), e);
- }
+
+ final BindingDescriptor descriptor = (BindingDescriptor) bindingContext.getBindingDescriptor();
+ if (descriptor.getEncoderBeanId() != null) {
+ try {
+ return springContext.getRequestContext().getActiveFlow().getApplicationContext().getBean(
+ descriptor.getEncoderBeanId(), MessageEncoder.class);
+ } catch (final BeansException e) {
+ log.warn("Error instantiating message encoder from bean ID {}", descriptor.getEncoderBeanId(), e);
}
}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SOAPLogoutRequest.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SOAPLogoutRequest.java
index 8f1defc..fb7770f 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SOAPLogoutRequest.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SOAPLogoutRequest.java
@@ -95,7 +95,7 @@ public class SOAPLogoutRequest extends AbstractProfileAction {
/** Strategy function for access to {@link SAMLMetadataContext} for input to SOAP client. */
@Nonnull private Function<ProfileRequestContext,SAMLMetadataContext> metadataContextLookupStrategy;
- /** Strategy function for access to {@link SAMLEndpointContext} to populate. */
+ /** Strategy function for access to {@link SAMLEndpointContext} to retrieve address from. */
@Nonnull private Function<ProfileRequestContext,SAMLEndpointContext> endpointContextLookupStrategy;
/** SOAP client. */
@@ -309,11 +309,16 @@ public class SOAPLogoutRequest extends AbstractProfileAction {
@Nonnull final LogoutResponse response) {
final Status status = response.getStatus();
if (status != null) {
- final StatusCode code = status.getStatusCode();
+ StatusCode code = status.getStatusCode();
if (code != null) {
if (StatusCode.SUCCESS.equals(code.getValue())) {
- log.debug("{} LogoutResponse was successful", getLogPrefix());
- propagationContext.setResult(Result.Success);
+ code = code.getStatusCode();
+ if (code == null || code.getValue() == null || !StatusCode.PARTIAL_LOGOUT.equals(code.getValue())) {
+ log.debug("{} Logout successful", getLogPrefix());
+ propagationContext.setResult(Result.Success);
+ } else {
+ log.debug("{} Logout partially successful", getLogPrefix());
+ }
return;
}
log.warn("{} LogoutResponse received with status code '{}'", getLogPrefix(), code.getValue());
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContext.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContext.java
index 18318f4..34cfca8 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContext.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/session/impl/PrepareInboundMessageContext.java
@@ -94,7 +94,7 @@ public class PrepareInboundMessageContext extends AbstractProfileAction {
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
} else if (logoutPropCtx.getSession() == null || !(logoutPropCtx.getSession() instanceof SAML2SPSession)) {
- log.debug("{} Logout propgation context did not contain a SAML2SPSession", getLogPrefix());
+ log.debug("{} Logout propagation context did not contain a SAML2SPSession", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return false;
}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForErrorTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForErrorTest.java
index 30c6615..566555a 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForErrorTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/InitializeOutboundMessageContextForErrorTest.java
@@ -21,6 +21,7 @@ import net.shibboleth.idp.profile.ActionTestingSupport;
import net.shibboleth.idp.profile.RequestContextBuilder;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.saml.binding.BindingDescriptor;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import org.opensaml.core.OpenSAMLInitBaseTestCase;
@@ -55,7 +56,10 @@ public class InitializeOutboundMessageContextForErrorTest extends OpenSAMLInitBa
prc = new WebflowRequestContextProfileRequestContextLookup().apply(src);
prc.setOutboundMessageContext(null);
action = new InitializeOutboundMessageContextForError();
- action.setOutboundBinding(SAMLConstants.SAML1_SOAP11_BINDING_URI);
+ final BindingDescriptor bd = new BindingDescriptor();
+ bd.setId(SAMLConstants.SAML1_SOAP11_BINDING_URI);
+ bd.initialize();
+ action.setOutboundBinding(bd);
action.initialize();
}
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContextsTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContextsTest.java
index 9438dbc..1bd4508 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContextsTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/profile/impl/PopulateBindingAndEndpointContextsTest.java
@@ -36,6 +36,7 @@ import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileR
import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
import net.shibboleth.utilities.java.support.xml.XMLParserException;
import org.opensaml.core.xml.XMLObjectBaseTestCase;
@@ -105,7 +106,7 @@ public class PopulateBindingAndEndpointContextsTest extends XMLObjectBaseTestCas
bindings.add(new BindingDescriptor());
bindings.get(0).setId(SAMLConstants.SAML2_POST_BINDING_URI);
bindings.get(0).initialize();
- action.setBindings(bindings);
+ action.setBindingDescriptorsLookupStrategy(FunctionSupport.<ProfileRequestContext,List<BindingDescriptor>>constant(bindings));
action.initialize();
}
@@ -138,7 +139,8 @@ public class PopulateBindingAndEndpointContextsTest extends XMLObjectBaseTestCas
binding.initialize();
final PopulateBindingAndEndpointContexts badaction = new PopulateBindingAndEndpointContexts();
badaction.setEndpointResolver(new DefaultEndpointResolver());
- badaction.setBindings(Collections.singletonList(binding));
+ badaction.setBindingDescriptorsLookupStrategy(
+ FunctionSupport.<ProfileRequestContext,List<BindingDescriptor>>constant(Collections.singletonList(binding)));
badaction.initialize();
final Event event = badaction.execute(rc);
@@ -287,7 +289,8 @@ public class PopulateBindingAndEndpointContextsTest extends XMLObjectBaseTestCas
binding.initialize();
final PopulateBindingAndEndpointContexts badaction = new PopulateBindingAndEndpointContexts();
badaction.setEndpointResolver(new DefaultEndpointResolver());
- badaction.setBindings(Collections.singletonList(binding));
+ badaction.setBindingDescriptorsLookupStrategy(
+ FunctionSupport.<ProfileRequestContext,List<BindingDescriptor>>constant(Collections.singletonList(binding)));
badaction.initialize();
final Event event = badaction.execute(rc);
diff --git a/idp-session-api/src/main/java/net/shibboleth/idp/session/context/navigate/LogoutContextSessionLookupFunction.java b/idp-session-api/src/main/java/net/shibboleth/idp/session/context/navigate/LogoutContextSessionLookupFunction.java
new file mode 100644
index 0000000..7809247
--- /dev/null
+++ b/idp-session-api/src/main/java/net/shibboleth/idp/session/context/navigate/LogoutContextSessionLookupFunction.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.idp.session.context.navigate;
+
+import java.util.Collection;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.session.SPSession;
+import net.shibboleth.idp.session.context.LogoutContext;
+
+import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+
+/**
+ * A function that returns a session from a {@link LogoutContext}
+ * and removes it from that context at the same time.
+ *
+ * <p>The exact session returned is unspecified.</p>
+ */
+public class LogoutContextSessionLookupFunction implements ContextDataLookupFunction<LogoutContext,SPSession> {
+
+ /** {@inheritDoc} */
+ @Nullable public SPSession apply(@Nullable final LogoutContext input) {
+
+ if (input != null) {
+ final Collection<SPSession> sessions = input.getSessionMap().values();
+ if (sessions != null && !sessions.isEmpty()) {
+ final SPSession session = sessions.iterator().next();
+ if (session != null) {
+ sessions.remove(session);
+ return session;
+ }
+ }
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/PopulateLogoutPropagationContext.java b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/PopulateLogoutPropagationContext.java
index 135d1ba..b427b10 100644
--- a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/PopulateLogoutPropagationContext.java
+++ b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/PopulateLogoutPropagationContext.java
@@ -27,9 +27,8 @@ import net.shibboleth.idp.session.SPSession;
import net.shibboleth.idp.session.SPSessionSerializerRegistry;
import net.shibboleth.idp.session.context.LogoutContext;
import net.shibboleth.idp.session.context.LogoutPropagationContext;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.idp.session.context.LogoutPropagationContext.Result;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.security.DataSealer;
@@ -49,14 +48,16 @@ import com.google.common.base.Function;
import org.springframework.webflow.execution.RequestContext;
/**
- * Profile action that creates a {@link LogoutPropagationContext} containing an {@link SPSession} to be destroyed. The
- * SP session may be populated by reference or value.
+ * Profile action that creates a {@link LogoutPropagationContext} containing {@link SPSession} to be destroyed. The
+ * SP sessions may be populated:
* <ul>
- * <li>By reference - via <em>SessionKey</em> request parameter that looks up {@link SPSession} from a
+ * <li>By reference - via <em>SessionKey</em> request parameter that looks up one {@link SPSession} from a
* {@link LogoutContext} stored in the HTTP session.</li>
* <li>By value - reconstitutes an encrypted {@link SPSession} object in <em>SPSession</em> request parameter.</li>
+ * <li>By lookup strategy.</li>
* </ul>
*
+ * @event SessionNotFound
* @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
* @event {@link org.opensaml.profile.action.EventIds#INVALID_PROFILE_CTX}
* @event {@link org.opensaml.profile.action.EventIds#UNABLE_TO_DECODE}
@@ -65,6 +66,9 @@ import org.springframework.webflow.execution.RequestContext;
public class PopulateLogoutPropagationContext extends AbstractProfileAction {
/** Name of parameter containing session by reference. */
+ @Nonnull @NotEmpty private static final String SESSION_NOT_FOUND = "SessionNotFound";
+
+ /** Name of parameter containing session by reference. */
@Nonnull @NotEmpty private static final String SESSION_PARAM_BYREF = "SessionKey";
/** Name of parameter containing session by value. */
@@ -77,10 +81,13 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
@Nullable private DataSealer dataSealer;
/** Mappings between a SPSession type and a serializer implementation. */
- @NonnullAfterInit private SPSessionSerializerRegistry spSessionSerializerRegistry;
+ @Nullable private SPSessionSerializerRegistry spSessionSerializerRegistry;
+
+ /** Lookup/creation function for LogoutPropagationContext. */
+ @Nonnull private Function<ProfileRequestContext,LogoutPropagationContext> contextCreationStrategy;
- /** Creation function for LogoutPropagationContext. */
- @Nonnull private Function<ProfileRequestContext, LogoutPropagationContext> contextCreationStrategy;
+ /** Lookup strategy for session. */
+ @Nullable private Function<ProfileRequestContext,SPSession> sessionLookupStrategy;
/** {@link SPSession} to operate on. */
@Nullable private SPSession session;
@@ -110,10 +117,10 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
*
* @param registry a registry of SPSession class to serializer mappings
*/
- public void setSPSessionSerializerRegistry(@Nonnull final SPSessionSerializerRegistry registry) {
+ public void setSPSessionSerializerRegistry(@Nullable final SPSessionSerializerRegistry registry) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- spSessionSerializerRegistry = Constraint.isNotNull(registry, "Registry cannot be null");
+ spSessionSerializerRegistry = registry;
}
/**
@@ -129,16 +136,18 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
"LogoutPropagationContext creation strategy cannot be null");
}
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
+ /**
+ * Set a lookup strategy to use to obtain the session to populate.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setSessionLookupStrategy(@Nullable final Function<ProfileRequestContext,SPSession> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- if (spSessionSerializerRegistry == null) {
- throw new ComponentInitializationException("SPSessionSerializerRegistry cannot be null");
- }
+ sessionLookupStrategy = strategy;
}
-
+
+// Checkstyle: ReturnCount|CyclomaticComplexity OFF
/** {@inheritDoc} */
@Override
protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -146,6 +155,18 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
if (!super.doPreExecute(profileRequestContext)) {
return false;
}
+
+ if (sessionLookupStrategy != null) {
+ session = sessionLookupStrategy.apply(profileRequestContext);
+ if (session != null) {
+ log.debug("{} Got session to propagate logout: {}", getLogPrefix(), session);
+ return true;
+ } else {
+ log.debug("{} No sessions remaining for logout propagation", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, SESSION_NOT_FOUND);
+ return false;
+ }
+ }
final RequestContext requestContext = getRequestContext(profileRequestContext);
if (requestContext == null) {
@@ -161,10 +182,11 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
sessionKey = sessionRef;
session = getSessionByReference(requestContext, sessionKey);
} else if (sessionVal != null) {
- if (dataSealer != null) {
+ if (dataSealer != null && spSessionSerializerRegistry != null) {
session = getSessionByValue(sessionVal);
} else {
- log.error("{} No DataSealer provided, unable to decrypt session passed by value", getLogPrefix());
+ log.error("{} No DataSealer/SerializerRegistry provided, unable to process session passed by value",
+ getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_DECODE);
return false;
}
@@ -186,18 +208,21 @@ public class PopulateLogoutPropagationContext extends AbstractProfileAction {
return true;
}
+// Checkstyle: ReturnCount|CyclomaticComplexity ON
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
final LogoutPropagationContext logoutPropCtx = contextCreationStrategy.apply(profileRequestContext);
if (logoutPropCtx == null) {
- log.error("{} Unable to create or locate SingleLogoutContext", getLogPrefix());
+ log.error("{} Unable to create or locate LogoutPropagationContext", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
return;
}
logoutPropCtx.setSession(session);
logoutPropCtx.setSessionKey(sessionKey);
+ logoutPropCtx.setResult(Result.Failure);
+ logoutPropCtx.setDetail(null);
}
/**
diff --git a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/RestoreProfileRequestContextTree.java b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/RestoreProfileRequestContextTree.java
new file mode 100644
index 0000000..b5d2f31
--- /dev/null
+++ b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/RestoreProfileRequestContextTree.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.idp.session.impl;
+
+import com.google.common.base.Function;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.ActionSupport;
+import net.shibboleth.idp.profile.context.AuditContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.session.context.LogoutContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.opensaml.messaging.context.InOutOperationContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Restores specific portions of the context tree used during logout processing to enable
+ * reuse of logout propagation subflows during back channel logout.
+ *
+ * <p>Some propagation flows make use of substantial portions of the tree, including the
+ * inbound/outbound contexts, audit context, relying party context, etc., so this amounts to
+ * a "push/pop" to preserve the state of the "outer" logout operation.</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ *
+ * @pre LogoutContext.getSubcontext(InOutOperationContext.class) != null
+ * @post LogoutContext.getSubcontext(InOutOperationContext.class) == null
+ */
+public class RestoreProfileRequestContextTree extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(RestoreProfileRequestContextTree.class);
+
+ /** Looks up a LogoutContext from PRC. */
+ @Nonnull private Function<ProfileRequestContext,LogoutContext> logoutContextLookupStrategy;
+
+ /** Constructor. */
+ public RestoreProfileRequestContextTree() {
+ logoutContextLookupStrategy = new ChildContextLookup<>(LogoutContext.class);
+ }
+
+ /**
+ * Set the lookup strategy for the {@link LogoutContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLogoutContextLookupStrategy(@Nonnull final Function<ProfileRequestContext,LogoutContext> strategy) {
+ logoutContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ final LogoutContext logoutContext = logoutContextLookupStrategy.apply(profileRequestContext);
+ if (logoutContext == null) {
+ log.debug("{} LogoutContext not found in ProfileRequestContext", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+
+ final InOutOperationContext placeholder = logoutContext.getSubcontext(InOutOperationContext.class);
+ if (placeholder == null) {
+ log.debug("{} InOutOperationContext not found under LogoutContext, nothing to do", getLogPrefix());
+ return;
+ }
+
+ logoutContext.removeSubcontext(placeholder);
+
+ profileRequestContext.setInboundMessageContext(placeholder.getInboundMessageContext());
+ profileRequestContext.setOutboundMessageContext(placeholder.getOutboundMessageContext());
+
+ final RelyingPartyContext relyingPartyCtx = placeholder.getSubcontext(RelyingPartyContext.class);
+ if (relyingPartyCtx != null) {
+ profileRequestContext.addSubcontext(relyingPartyCtx, true);
+ }
+
+ final AuditContext auditCtx = placeholder.getSubcontext(AuditContext.class);
+ if (auditCtx != null) {
+ profileRequestContext.addSubcontext(auditCtx, true);
+ }
+
+ log.debug("{} Restored LogoutRequest processing state after logout propagation", getLogPrefix());
+ }
+
+}
\ No newline at end of file
diff --git a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SaveProfileRequestContextTree.java b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SaveProfileRequestContextTree.java
new file mode 100644
index 0000000..a84b49d
--- /dev/null
+++ b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SaveProfileRequestContextTree.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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.idp.session.impl;
+
+import com.google.common.base.Function;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.ActionSupport;
+import net.shibboleth.idp.profile.context.AuditContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.session.context.LogoutContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.opensaml.messaging.context.InOutOperationContext;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Saves off specific portions of the context tree in use during logout processing to enable
+ * reuse of logout propagation subflows during back channel logout.
+ *
+ * <p>Some propagation flows make use of substantial portions of the tree, including the
+ * inbound/outbound contexts, audit context, relying party context, etc., so this amounts to
+ * a "push/pop" to preserve the state of the "outer" logout operation.</p>
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ *
+ * @post LogoutContext.getSubcontext(InOutOperationContext.class) != null
+ */
+public class SaveProfileRequestContextTree extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(SaveProfileRequestContextTree.class);
+
+ /** Looks up a LogoutContext from PRC. */
+ @Nonnull private Function<ProfileRequestContext,LogoutContext> logoutContextLookupStrategy;
+
+ /** Constructor. */
+ public SaveProfileRequestContextTree() {
+ logoutContextLookupStrategy = new ChildContextLookup<>(LogoutContext.class);
+ }
+
+ /**
+ * Set the lookup strategy for the {@link LogoutContext}.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLogoutContextLookupStrategy(@Nonnull final Function<ProfileRequestContext,LogoutContext> strategy) {
+ logoutContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+ final LogoutContext logoutContext = logoutContextLookupStrategy.apply(profileRequestContext);
+ if (logoutContext == null) {
+ log.debug("{} LogoutContext not found in ProfileRequestContext", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ return;
+ }
+
+ final MessageContext inbound = profileRequestContext.getInboundMessageContext();
+ final MessageContext outbound = profileRequestContext.getOutboundMessageContext();
+
+ final InOutOperationContext placeholder = new InOutOperationContext(inbound, outbound);
+ logoutContext.addSubcontext(placeholder);
+ profileRequestContext.setInboundMessageContext(null);
+ profileRequestContext.setOutboundMessageContext(null);
+
+ final RelyingPartyContext relyingPartyCtx = profileRequestContext.getSubcontext(RelyingPartyContext.class);
+ if (relyingPartyCtx != null) {
+ placeholder.addSubcontext(relyingPartyCtx);
+ }
+
+ final AuditContext auditCtx = profileRequestContext.getSubcontext(AuditContext.class);
+ if (auditCtx != null) {
+ placeholder.addSubcontext(auditCtx);
+ }
+
+ log.debug("{} Saved off LogoutRequest processing state during logout propagation", getLogPrefix());
+ }
+
+}
\ No newline at end of file
diff --git a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SelectLogoutPropagationFlow.java b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SelectLogoutPropagationFlow.java
index 4504460..e77ed54 100644
--- a/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SelectLogoutPropagationFlow.java
+++ b/idp-session-impl/src/main/java/net/shibboleth/idp/session/impl/SelectLogoutPropagationFlow.java
@@ -28,7 +28,6 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,7 +54,7 @@ public class SelectLogoutPropagationFlow extends AbstractProfileAction {
@Nonnull private final Logger log = LoggerFactory.getLogger(SelectLogoutPropagationFlow.class);
/** Selection function to determine suitable LogoutPropagationFlowDescriptor for given SPSession. */
- @Nonnull private final Function<SPSession, LogoutPropagationFlowDescriptor> flowSelectorFunction;
+ @Nonnull private final Function<SPSession,LogoutPropagationFlowDescriptor> flowSelectorFunction;
/** Function to retrieve LogoutPropagationContext from context tree. */
@Nonnull private Function<ProfileRequestContext, LogoutPropagationContext> logoutPropagationContextFunction;
@@ -80,20 +79,19 @@ public class SelectLogoutPropagationFlow extends AbstractProfileAction {
logoutPropagationContextFunction = Constraint.isNotNull(function, "Function cannot be null");
}
- // Checkstyle: ReturnCount OFF
/** {@inheritDoc} */
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
final LogoutPropagationContext logoutPropCtx = logoutPropagationContextFunction.apply(profileRequestContext);
if (logoutPropCtx == null || logoutPropCtx.getSession() == null) {
log.error("{} LogoutPropagationContext not found or found with null SPSession", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+ ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_POTENTIAL_FLOW);
return;
}
final LogoutPropagationFlowDescriptor flow = flowSelectorFunction.apply(logoutPropCtx.getSession());
if (flow == null) {
- log.error("{} No potential flows to choose from, logout propagation will fail", getLogPrefix());
+ log.error("{} No potential flows to choose from, no logout propagation possible", getLogPrefix());
ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_POTENTIAL_FLOW);
return;
}
@@ -101,6 +99,5 @@ public class SelectLogoutPropagationFlow extends AbstractProfileAction {
log.debug("{} Selecting logout propagation flow {}", getLogPrefix(), flow.getId());
ActionSupport.buildEvent(profileRequestContext, flow.getId());
}
- // Checkstyle: ReturnCount ON
}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list