The ScriptedAttribute attribute definition constructs an output attribute via the execution of a JSR-223 script.

Prior to V3.3, the xsi:type used was Script, declared in the urn:mace:shibboleth:2.0:resolver:ad namespace.

Java 8 uses an updated language engine for ECMA scripting that has incompatibilities with ECMA scripts written for the older Rhino engine included with Java 7 and older IdP versions. Some of the differences are noted in the examples below.

Schema Name and Location

This xsi:type is defined by the urn:mace:shibboleth:2.0:resolver namespace 3.3, the schema for which can be located at http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd

Prior to V3.3 supplied plugins were defined by a schema type in the urn:mace:shibboleth:2.0:resolver:ad namespace, the schema for which is located at http://shibboleth.net/schema/idp/shibboleth-attribute-resolver-ad.xsd. This is still supported, but every element or type in the old namespace has an equivalently named (but not necessarily identical) version in the urn:mace:shibboleth:2.0:resolver namespace. The use of the urn:mace:shibboleth:2.0:resolver namespace also allows a relaxation of the ordering requirements of child elements to reduce strictness.

Attributes

Any of the common attributes can be specified. In addition, the following attributes may be provided:

Name
Type
Default
Description
language          
stringJavaScriptDefines the JSR-223 language to use. The default is ECMA script using either the Rhino (Java 7) or Nashorn (Java 8) engines.

customObjectRef 3.2

string
The name of a Spring Bean defined elsewhere. This bean will be made available to the script in a variable named "custom".

Child Elements

Any of the common child elements can be specified. In addition, one of the following two elements must be defined

NameCardinalityDescription
<Script>


Exactly One

The content (typically inside an XML CDATA block) defines the actual Script to be executed
<ScriptFile>
The content specifies a file which contains the script to be executed

Notes On the Semantics of the Script

Data Available to the script

The script has the following available:

The ScriptedIdPAttribute

The attribute variables (both input and output) available to the scripting environment have the following methods. Note that more methods may be added to this interface between minor software revisions, but methods will not be removed except between major revisions, if at all.

Adding Values

Values are added to the attributed by calling the addValue() method. This must take either a String value or an object which implements IdPAttributeValue. The former is the most common case. A useful example of the latter is ScopedStringAttributeValue which has two constructor parameters - the value and scope, respectively. There are also other subclasses available.

Thus:

eduPersonEntitlement.addValue("urn:mace:dir:entitlement:common-lib-terms");
scopedValueType =  Java.type("net.shibboleth.idp.attribute.ScopedStringAttributeValue");
eduPersonPrincipalName.addValue(new scopedValueType("user", "example.org"));

Locating Other Contexts

The standard way of locating other context types in the tree of state information is via context navigation. The following example shows how to locate a peer context and a child context (the actual context types shown are examples only):

child = resolutionContext.getSubcontext("net.shibboleth.idp.attribute.resolver.ChildContextType"); 
parent = resolutionContext.getParent();
peer = parent.getSubcontext("net.shibboleth.idp.attribute.resolver.PeerContextType");

Logging Within a Script

The same logging framework used throughout the IdP (SLF4J) may be used for logging within a script. First import the package org.slf4j and then obtain an org.slf4j.Logger object from an org.slf4j.LoggerFactory. The logging category name used is arbitrary, but you may need to adjust the IdP's logging configuration to see particular results. Logging levels available are: error, warn, info, debug, trace.

The string passed to the LoggerFactory.getLogger method should be the name of an existing logger element, defined in logging.xml.

For more information on configuring logging within the IdP, see the LoggingConfiguration topic.

importPackage(Packages.org.slf4j);
 
logger = LoggerFactory.getLogger("net.shibboleth.idp.attribute");
Scripted.addValue("foo");
Scripted.addValue("bar");

logger.info("Values of scriptTest were: {} ", Scripted.getValues());
logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute");

Scripted.addValue("foo");
Scripted.addValue("bar");

logger.info("Values of scriptTest were: {} ", Scripted.getValues());

Accessing other information

The use of the customObjectRef is a very powerful paradigm, widening the scope of information available to a script beyond the request's immediate state (via the profile request context).

In particular, the beans named "shibboleth.HttpServletRequest" and "shibboleth.HttpServletResponse" allow access to the HTTP information associated with the request and response.

V2 Compatibility

V2 Compatibility is deprecated.


In order to support the majority of scripts written for V2, the runtime environment is extended in two ways:

It is expected that the addition of this emulation code will allow the majority of V2 scripts to run unchanged. All of the examples in the V2 wiki topic run unchanged (subject to the constraints introduced by Java 1.8). Nonetheless, users should consider rewriting their scripts after upgrading, as this capability will likely be removed in V4.

Java 1.8 and Nashorn

In Java 1.8, a new scripting engine is provided for the default "JavaScript" language. This has some syntactic (explained here) and semantic differences.  To convert a working V2, pre-Java 1.8 script to IdPV3 running Java 1.8:

Of course, for new scripts created for V3 alone, this isn't necessary.

Examples

Get eduPersonPrincipalName from LDAP or build one from uid

Variant 1: A "Prescoped" AttributeDefinition resolves existing eduPersonPrincipalName values from LDAP, plus it depends on the "ScriptedAttribute" one to generate missing values. The ScriptedAttribute definition has a dependency on the myLDAP DataConnector in order to have access to any eduPersonPrincipalName and uid attribute values.

 <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Prescoped">
    <InputAttributeDefinition ref="eppnFromUid" />
    <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
    <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
</AttributeDefinition>

<AttributeDefinition id="eppnFromUid" xsi:type="ScriptedAttribute" dependencyOnly="true">
    <InputDataConnector ref="myLDAP" attributeNames="eduPersonPrincipalName uid" />
    <Script><![CDATA[if (typeof eduPersonPrincipalName == "undefined") eppnFromUid.addValue(uid.getValues().get(0) + "@%{idp.scope}");]]></Script>
</AttributeDefinition>

Variant 2: Doing everything in one ScriptedAttribute type AttributeDefinition. Since the eduPersonPrincipalName values from LDAP will be a prescoped, but to the IDP "flat", string we'll have to empty out the Collection of values before adding the properly scoped one based on ScopedStringAttributeValue (described above).

<AttributeDefinition id="eduPersonPrincipalName" xsi:type="ScriptedAttribute">
    <InputDataConnector ref="myLDAP" attributeNames="eduPersonPrincipalName uid" />

    <Script><![CDATA[
logger = Java.type("org.slf4j.LoggerFactory").getLogger("net.shibboleth.idp.attribute.resolver.eppnbuilder");
scopedValueType =  Java.type("net.shibboleth.idp.attribute.ScopedStringAttributeValue");
var localpart = "";
if (typeof eduPersonPrincipalName == "undefined" || eduPersonPrincipalName.getValues().size() < 1) {
    logger.debug("No ePPN in LDAP found, creating one");
    localpart = uid.getValues().get(0);
} else {
    logger.debug("ePPN had value: " + eduPersonPrincipalName.getValues().get(0));
    localpart = eduPersonPrincipalName.getValues().get(0).split("@")[0];
    eduPersonPrincipalName.getValues().retainAll([]);
}
eduPersonPrincipalName.addValue(new scopedValueType(localpart, "%{idp.scope}"));
logger.debug("ePPN final value: " + eduPersonPrincipalName.getValues().get(0));
    ]]></Script>

    <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
    <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />

</AttributeDefinition>