Adding arbitrary attributes to the audit log

It can be useful to have a point-in-time record of specific attributes, particularly generated attributes, for audit purposes. The Shibboleth Identity Provider (IdP) audit log provides an appropriate location and is probably already being centrally monitored for other purposes.

This example shows how to add the eduPersonTargetedId attribute as an extra field to the Shibboleth Identity Provider's audit log (idp-audit.xml). This could also be used for other attributes such as pairwise-id.

Assumptions

Your audit log configuration is unchanged from a v4.0 installation and your attribute resolver generates an attribute named eduPersonTargetedId such as:

<AttributeDefinition id="eduPersonTargetedId" xsi:type="SAML2NameID" nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"> <InputDataConnector attributeNames="computedId" ref="computed"/> <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10"/> <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedId"/> </AttributeDefinition>

Implementation

Create a new bean to make arbitrary attributes available

In conf/audit.xml, add the following <beans> (at the end, before the closing </beans>). This script, based on the examples in AuditLoggingConfiguration, takes a single or multi-valued attribute generated by the resolver and returns it to a calling bean. If the attribute is multi-valued then it delimits the values with a comma (,) in the same way as Perl's join(',', @list).

<bean id="AttributeValueExtraction" parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript" abstract="true"> <constructor-arg> <value> <![CDATA[ var getinfo = function() { var rpContext = input.getSubcontext("net.shibboleth.idp.profile.context.RelyingPartyContext"); if (null === rpContext) { return null; } var attrContext = rpContext.getSubcontext("net.shibboleth.idp.attribute.context.AttributeContext"); if (null === attrContext) { return null; } var attributes = null attributes = attrContext.getUnfilteredIdPAttributes(); if (null === attributes) { return null; } attribute = attributes.get(custom); if (null === attribute || attribute.getValues().isEmpty()) { return null; } var iter = attribute.getValues().iterator(); var result = ""; while (iter.hasNext()) { if(!result.equals("")) result = result + ","; result = result + iter.next().getDisplayValue(); } return result; } getinfo(); ]]> </value> </constructor-arg> </bean>

Create a sub-bean for the specific attribute required

After the AttributeValueExtraction bean, add:

<bean id="audit-eduPersonTargetedId" parent="AttributeValueExtraction" p:customObject="eduPersonTargetedId" />

You can create multiple audit-... beans if you require multiple attributes to be made available in the audit log.

Create the audit log value

After the audit-eduPersonTargetedID bean, add:

This creates a new substitution value %eduPersonTargetedID based on the value returned from audit-eduPersonTargetedID.

Update the Audit log template to include the new value

In conf/audit.xml, find <util:map id="shibboleth.AuditFormattingMap"> which may look like:

Add the new attribute (%eduPersonTargetedID)to the end of the template:

and restart the IdP.