Concepts
A widespread pattern throughout much of the IdP configuration is the concept of an "activation condition". This is a mechanism by which functions and settings can be selectively enabled or disabled at runtime based on an arbitrary condition that is evaluated for each request. This addresses the ability to easily extend the behavior of components and handle new use cases. For consistency, we've tried to use the property name "activationCondition" across many of the components with this feature.
An activation condition, formally, is a Java bean of type Predicate<ProfileRequestContext>
. A Predicate is a very simple generic interface that implements a single method:
public interface Predicate<T> { boolean apply(T input) }
That is, it lets the IdP run a method against an input object to get a yes/no answer. The input in the majority of cases is the "root" object of the context state tree that tracks a request from start to finish, of type org.opensaml.profile.context.ProfileRequestContext. By consistently basing everything off of that abstraction, it's possible to build up a library of condition objects that can be used in many different places to implement some kind of check.
The IdP includes a fairly extensive library of such conditions, and there are a number of pre-defined beans provided to help make configuration simpler for a lot of common use cases and we can extend that set over time (and it's easy to submit more examples here that people can cut and paste).
We also supply public implementations of this interface such that the logic can be supplied via a script or via a Spring Expression, avoiding the overhead of creating a new Java class in a separate extension library.
Use Cases
Some of the advanced uses you might have for this feature include:
Creating custom relying party overrides beyond the ones supplied
Limiting when authentication flows may be used
Limiting when NameID generation or consumption plugins may be used
Controlling the use of attribute resolver connectors, definitions, or encoders
Determining when profile intercept flows run, such as attribute consent
Extending the attribute filter policy language
In most cases, using this feature amounts to setting a bean implementing the condition you want into a component bean's activationCondition
property. With the older configuration files that are not Spring bean files, there will usually be some kind of XML attribute used to reference to condition (e.g. activationConditionRef
) and you will have to define the condition bean in a separate file. This isn't ideal, but it allows for essentially any condition to be attached without regard for how complex its own configuration might be.
Defining the condition bean
The location where the condition bean would be defined depends on the use case. It's generally safe to define them in global.xml, as long as the naming is unique. For the most isolation, and to support reloadability of their definitions, it's better to define them inside specific service contexts when possible, generally by adding a bean resource file to the set of resources defined in services.xml for a given service.
Attaching Conditions
The various examples below demonstrate the creation of stand-alone beans of the appropiate type. To actually use them in specific cases typically requires injecting the bean ID into a property named activationCondition-ref
in another bean, or using a custom XML attribute.
Native Spring Syntax
For instance, attaching a condition bean named "MyCondition" to control the execution of a NameID generator bean in conf/saml-nameid.xml looks like this:
<bean parent="shibboleth.SAML2AttributeSourcedGenerator" p:format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" p:attributeSourceIds="#{ {'mail'} }" p:activationCondition-ref="MyCondition" />
Custom Syntax
In contrast, attaching a condition to an attribute defintion looks like this:
<AttributeDefinition xsi:type="Simple" id="attr1" sourceAttributeID="attr1source" activationConditionRef="MyCondition"> <resolver:Dependency ref="staticAttributes" /> </AttributeDefinition>
In this case the bean definition for the activation condition (MyCondition
above) should be included in a suitable native spring file.
Simple conditions, and those requiring to be reloadable, should be placed in a standalone file loaded via conf/services.xml.
This file must be supplied to the appropriate resource list in conf/services.xml. For instance for the Attribute Resolver:
<util:list id ="shibboleth.AttributeResolverResources"> <value>${idp.home}/conf/attribute-resolver.xml</value> <value>${idp.home}/conf/THE_NEW_FILE.xml</value> </util:list>
Complex beans that will never need to be reloaded can be put in conf/global.xml (or a file imported into it).
Examples
If you develop useful examples, please consider adding them to this page. The examples below are not fully tested in a few cases, so please report any problems using them to the mailing list or make corrections as you find them.
Specific Relying Parties
This is a common use case, and simple to grasp, so we use it to demonstrate a variety of different approaches that you can apply to other use cases once the syntax makes sense to you.
A built-in bean is provided to make this use case simple. You should provide a list of names, providing a single name may not have the expected result.
A more advanced option supported by the same parent bean is the ability to plug-in an arbitrary condition to run against the relying party name. The input to such a condition is a String rather than the ProfileRequestContext.
You can also write a script (Javascript being the default language):
Finally, you can write Spring Expressions, sort of a more concise scripting format:
Relying Parties By Group
In this context, grouping RPs is historically done by containing SAML metadata entities in <md:EntitiesDescriptor>
elements, something common to aggregated files produced by many federations. This is more of a historical use case, because it's dangerous to use with aggregates you don't control. The containment model is very limiting and often doesn't mean what people think it does, so it's better to use the "tag" approach illustrated below.
But for aggregates you control locally, it's a simple way to apply policy and solve certain problems, such as grouping together SAML 1-only systems or grouping systems that don't support encryption, or grouping together systems by their attribute needs.
As of V3.4, group matching is extended to check for <AffiliationDescriptor>
content in metadata, which is a way of providing explicit "out of band" groups of entities (analagous to creating an LDAP group that lists its members, rather than embedding the memberships into each member's entry).
Most use cases for this feature tend to be for relying party overrides, which are already supported separately. If you need to use this kind of condition elsewhere, you can reuse the same code with this example:
Relying Parties By Tag
The "modern" approach to attaching policy to relying parties is to leverage so-called tags, which refers to a metadata extension called an "Entity Attribute", a SAML attribute embedded in a system's metadata that is about the system rather than an individual user. Entity attributes have a variety of use cases, essentially anything one can think of saying about a system, and they're more flexible than groups because they can be attached directly to the entity's metadata regardless of where it's published. If you will, this is the equivalent of an LDAP "memberOf" attribute as opposed to a directory object that contains all the members of a group.
Most use cases for this feature tend to be for relying party overrides, which are already supported separately. If you need to use this kind of condition elsewhere, you can reuse the same code with this example. The example shows the use of two different tag “values” to check for, one that’s standardized and one that’s custom, just to show how to handle two values at once.
Client Address Ranges
Some components may be sensitive to the address of the client, perhaps to distinguish users running on an enterprise network vs. working remotely. A condition class has been created to make this check simple using CIDR masks. Note that a servlet request bean declared by the Shibboleth software MUST be injected into any bean that needs access to the current request (the same is true of the servlet response in other cases).
Address Range Examples
<bean id="MyCondition" class="org.opensaml.profile.logic.IPRangePredicate" p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier" p:ranges="#{ {'192.168.1.0/24', '192.168.2.0/28'} }" />
See this link for more details
Attribute Checking
Some components may need to check for the presence (or absence) of a particular attribute or value for a user. A basic condition is provided for this purpose, or may be a useful code example to follow to implement something more complex.
The class provided supports only simple string-valued attributes, and supports a simple form of wildcarding to indicate that any value is acceptable as long as one exists.
Compound Boolean Conditions
The usual boolean operators (AND/OR/NOT) are available as predicates to allow the construction of complex compound conditions out of inividual parts. It's somewhat similar in structure to using boolean operators in the attribute filter policy language. It is of course possible to arbitrarily nest AND/OR/NOT conditions together to create complex expressions, although the (lack of) readability is such that it may be easier to use a script.
The first example demonstrates an OR operation that is equivalent to earlier examples but illustrates the syntax. The argument to the condition is a collection of condition beans to OR together.
The second example demonstrates a compound condition that checks a specific SP AND for a particular client network range.
Finally, a simple NOT example checking for any SP except for one: