The Shibboleth IdP V3 software has reached its End of Life and is no longer supported. This documentation is available for historical purposes only. See the IDP4 wiki space for current documentation on the supported version.
ScriptedAttributeDefinition
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 | string | JavaScript | Defines the JSR-223 language to use. The default is ECMA script using either the Rhino (Java 7) or Nashorn (Java 8) engines. |
| 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
Name | Cardinality | Description |
---|---|---|
<Script> |
| 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:
- A variable which implements ScriptedIdPAttribute whose name is the ID of the attribute definition.
Note, if an attribute of this name occurs as a dependency (see below), then the attribute will be pre-populated with all the values of the dependent attribute. - A variable named resolutionContext containing the AttributeResolutionContext for the current resolution request.
- A variable named profileContext containing the ProfileRequestContext for the current resolution request.
- A variable named custom 3.2, which contains whatever was provided by the
customObjectRef
attribute (see above) - A variable named subjects 3.3, an array of the java
javax.security.auth.Subject
objects associated with this authorization. Note that these will only be present if the attribute resolution has been associated with an act of authentication (and so will not work for back channel requests). - Other contexts can be located by navigating the context tree, whose root is the profileContext variable.
- A variable which implements ScriptedIdPAttribute for each attribute produced by the defined dependencies of this definition. If a dependency is an attribute definition, that attribute is supplied. If a dependency is a data connector, every attribute resolved by that connector is supplied. The variable's name will be that of the ID of the attribute from the dependency. In the event that more than one dependency produces attributes with the same ID, the values of all of those attributes will be merged and made available to the script.
Note that any changes made to these attributes within the script will not be reflected in the result of the attribute resolution. - A single attribute, whose name is the ID of the attribute definition, is made available at the end of the attribute resolution process.
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.
Collection getValues()
- This returns the current values of the attribute (which may be empty). As a convenience, StringAttributeValues are available as simple Strings, but all other AttributeValue types are only available as their native type since their underlying data can only be accessed natively. Values can be added and removed from this collection, but added values must be of type String or implement the IdPAttributeValue interface. This method cannot be called if
getNativeAttribute()
has been called on the same attribute in the same script.
- This returns the current values of the attribute (which may be empty). As a convenience, StringAttributeValues are available as simple Strings, but all other AttributeValue types are only available as their native type since their underlying data can only be accessed natively. Values can be added and removed from this collection, but added values must be of type String or implement the IdPAttributeValue interface. This method cannot be called if
IdPAttribute getNativeAttribute()
- Provided for advanced use only, this returns a V3 native IdPAttribute. Note that only the output attribute will be available after the script has run. This method cannot be called if
getValues()
has been called.
- Provided for advanced use only, this returns a V3 native IdPAttribute. Note that only the output attribute will be available after the script has run. This method cannot be called if
addValue(value)
- This adds a value to the attribute. The parameter either be a String or an implementation of IdPAttributeValue. See below for examples.
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:
- A package named edu.internet2.middleware.shibboleth.common.attribute.provider is available, and specifically within it, the BasicAttribute class. This provides a facsimile of the V2-equivalent class. Specifically:
- A constructor which takes a String, being the name of the attribute being created.
- The
getValues()
method which returns the current values. - This would only be of use in rhino scripting. see below
- A variable named requestContext which implements all the methods implemented by the V2 class "edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext". However, all but three of these methods do nothing but log an error and (where a return value is required) return a null pointer. The three methods which are implemented are:
- getPrincipalName to provide identification of the subject. This should be replaced by
resolutionContext.getPrincipal()
- getPeerEntityId to get the entityId of the attribute recipient. This should be replaced by
resolutionContext.getAttributeRecipientID()
- getLocalEntityId to get the entityId of the attribute issuer. This should be replaced by
resolutionContext.getAttributeIssuerID()
- getPrincipalName to provide identification of the subject. This should be replaced by
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:
- Make the syntactic changes required.
Remove the now-redundant creation of the output attribute (In V3 the output attribute is precreated). If the script needs to run under Java 1.7 and Java 1.8, the creation can be conditionalized:
if (null == scriptedAttribute) { // will only occurr under IdP V2, running pre Java 1.8 scriptedAttribute = new BasicAttribute("scriptedAttribute"); }
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>