Merging attributes in Shibboleth 2 IdP

I’ve not written here for a while but, as I couldn’t find anything about this anywhere on the internet at large and I needed to do it, I suspect someone else might need to do this too so here it is.

The problem

I wanted to take two arbitrary attributes sourced, in this case, from our LDAP service and merge them into one single Shibboleth eduPerson attribute (eduPersonEntitlement in this case) for later filtering and release to Service Providers.

The frustration

There are plenty of magic things you can do to attributes within the Shibboleth IdP 2 application like mapping based on value, regex manipulation and all sorts (for the gory details, see the Shibboleth Wiki) which all act on a single attribute — there isn’t a “Merge” function which allows work on multiple attributes.

Having asked on a couple of mailing lists, this was confirmed along with…

The solution

As Chad points out in his reply to my question, the IdP engine has a scripting language built in (JavaScript[ish]) which can perform arbitrary operations on data.

It turns out that it’s relatively easy to do exactly what I want! Woo!

In the below, I will be merging attribute B into attribute A. It would also be possible to merge A and B into C — you’ll get the idea.

Preparation

  1. Ensure the DataConnector is pulling in the required attributes from your data source (LDAP in my case):
    <resolver:DataConnector id="myLDAP" ...>
       <ReturnAttributes>A B</ReturnAttributes>
    </resolver:DataConnector>
  2. Pull the second attribute (B) into the XML data model so it can be used next:
    <resolver:AttributeDefinition id="B"
       xsi:type="Simple"
       xmlns="urn:mace:shibboleth:2.0:resolver:ad"
       sourceAttributeID="B">
    <resolver:Dependency ref="myLDAP" />
    </resolver:AttributeDefinition>

Script

importPackage(Packages.edu.internet2.middleware.shibboleth.common.attribute.provider);
importPackage(Packages.org.slf4j);
logger = LoggerFactory.getLogger("edu.internet2.middleware.shibboleth.resolver.Script.A");
logger.info("Values of A are: " + A.getValues());
if (typeof B != "undefined" && B != null ){
 logger.info("Values of B are: " + B.getValues());
 A.getValues().addAll(B.getValues());
} else {
 logger.info("B is not defined");
}
logger.info("Values of A are: " + A.getValues());

Knitting the script in

The script then needs including in the resolver config:

<resolver:AttributeDefinition
 xsi:type="Script"
 xmlns="urn:mace:shibboleth:2.0:resolver:ad"
 id="A">
 <resolver:Dependency ref="myLDAP" />
 <!-- <resolver:AttributeEncoder> attributes will probably need including here -->
 <Script><![CDATA[
 // put script here
 ]]></Script>
</resolver:AttributeDefinition>

Attribute A now includes all the values from attribute B and can continue to be processed normally.

Other things I learnt (relearnt)

aacli is a useful tool for testing out attribute resolver and filter config!

$ aacli.sh --configDir=. --principal $USER --requester $SP_ENTITYID

will generate the appropriate SAML assertion for the supplied SP based on your config.

Leave a Reply