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. |
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.
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 ". |
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 |
The script has the following available:
customObjectRef
attribute (see above)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).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()
getNativeAttribute()
has been called on the same attribute in the same script.IdPAttribute getNativeAttribute()
getValues()
has been called.addValue(value)
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")); |
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"); |
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()); |
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 is deprecated. |
In order to support the majority of scripts written for V2, the runtime environment is extended in two ways:
getValues()
method which returns the current values.resolutionContext.getPrincipal()
resolutionContext.getAttributeRecipientID()
resolutionContext.getAttributeIssuerID()
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.
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:
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.
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> |