Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This flow can operate in a limited "single condition" mode, or a more flexible functional mode. The functional mode is strongly suggested.

Localtabgroup
Localtab live
activetrue
Expand
titleFunctional Mode

Previously, it was common to need to create copies of the flow to accommodate different needs. While this is possible, it can be avoided in essentially all cases by using the support for applying a Function instead of a single condition/predicate. While the condition mode applies a single condition and returns a fixed error Event, the Function mode is more general in that it directly returns a string to use as the follow up event.

A bean named shibboleth.context-check.Function in conf/intercept/context-check-intercept-config.xml must be defined by you with the function you want to apply. The bean must be of type Function<ProfileRequestContext,String>. The return value contains the event that the interceptor should signal, either "proceed" to indicate that processing should continue successfully or any other value as a custom Event. For example, returning "ContextCheckDenied" will match the existing behavior of the original condition mode.

If you want to support one or more custom events, you'll need to add the event(s) to conf/intercept/intercept-events-flow.xml. The default file includes a commented example for an event called "MyCustomEvent". Then you'll need to add that event in conf/errors.xml if you want it handled with a local error page.

As a primitive example, consider a map indexed by relying party name, allowing a condition to be uniquely defined for each applicable relying party separately.

Example function using a map of conditions
Code Block
languagexml
<util:map id="ConditionMap">
	<entry key="https://sp.example.org/sp">
		<ref bean="Condition1"/>
	</entry>
	<entry key="https://another.example.org/sp">
		<ref bean="Condition2"/>
	</entry>
</util:map>

<bean id="shibboleth.context-check.Function" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"
		p:customObject-ref="ConditionMap">
	<constructor-arg>
		<value>
		<![CDATA[
		var event = "proceed";
		var rpid = input.getSubcontext(
				"net.shibboleth.idp.profile.context.RelyingPartyContext").getRelyingPartyId();
		var condition = custom.get(rpid);
		if (condition != null && !condition.test(input)) {
			event = "ContextCheckDenied";
		}
		event;
		]]>
		</value>
	</constructor-arg>
</bean>
localtab-live
Expand
titleCondition Mode

The only configuration involved with this mode is to define the condition you want it to evaluate, and possibly adjust the user interface result in the event of failure.

The bean named shibboleth.context-check.Condition in conf/intercept/context-check-intercept-config.xml must be defined by you with the condition you want to apply. The bean must be of type Predicate<ProfileRequestContext>, but beyond that, it can do anything. Note that this is the same type signature as the conditions discussed in the ActivationConditions topic, so the examples there may help you. Non-programmers may be particularly interested in the scripted examples.

Tip

The common authorization usage for this flow is reflected in the example condition you will find in the file. It demonstrates the use of a built-in condition called a SimpleAttributePredicate, which evaluates the request for the presence of particular attribute(s) and optionally value(s) through a simple map. Each map entry is the ID of an attribute, and the map values are a list of values of that attribute to check for (or you can use an asterisk as a wildcard to indicate that any value is acceptable). Refer to the Javadoc for additional details.

The other half of the configuration of this flow is the result. Obviously success simply causes things to proceed in the usual fashion, but failure will produce an event called "ContextCheckDenied". The IdP's built-in ErrorHandlingConfiguration treats that event as a "local error", meaning that processing is halted and an error page displayed.

The event is mapped to a default error message using the standard machinery, which you can adjust, but producing the right response for a lot of different (possibly unrelated) scenarios will quickly become hard to manage. You will probably want to define your own events, and to do that, you should use the Function mode described earlier.

...

Expand
titleExample enforcing use of MFA based on user policy
Code Block
languagexml
<!--
	Returns true if a user's directory entity authorizes use of the "basic" profile or
	if the active results include the "mfa" profile constant.
	-->
    <bean id="shibboleth.context-check.Condition" parent="shibboleth.Conditions.OR">
        <constructor-arg>
            <list>
                <bean class="net.shibboleth.idp.profile.logic.SimpleAttributePredicate"
                        p:useUnfilteredAttributes="true">
                    <property name="attributeValueMap">
                        <map>
                            <entry key="eduPersonAssurance">
                                <list>
                                    <value>http://id.incommon.org/assurance/basic</value>
                                </list>
                            </entry>
                        </map>
                    </property>
                </bean>
                <ref bean="CheckForMFA" />
            </list>
        </constructor-arg>
    </bean>

	<!-- Checks all the active authentication results for the appropriate AuthnContextClassRefPrincipal. -->
	<bean id="CheckForMFA" parent="shibboleth.Conditions.Scripted" factory-method="inlineScript">
		<constructor-arg>
			<value>
<![CDATA[
			value = false;

			principalType = Java.type("net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal");

			subjectCtx = profileContext.getSubcontext("net.shibboleth.idp.authn.context.SubjectContext");
			if (subjectCtx != null) {
				var subjectIter = subjectCtx.getSubjects().iterator();
				while (!value && subjectIter.hasNext()) {
					var princIter = subjectIter.next().getPrincipals(principalType.class).iterator();
					while (!value && princIter.hasNext()) {
						if (princIter.next().getName() == "http://id.incommon.org/assurance/mfa") {
							value = true;
						}
					}
				}
			}

			value;
]]>
			</value>
		</constructor-arg>
	</bean>

Reference

...

Beans

Bean ID

Type

Description

shibboleth.context-check.Condition

Predicate<ProfileRequestContext>

Condition evaluated by the interceptor flow to decide whether to continue

shibboleth.context-check.Function  

Function<ProfileRequestContext,String>

Function evaluated by the interceptor flow to produce the event to signal