Page History
The Shibboleth IDP's Attribute Resolver collects data from authoritative sources (systems of record) and transforms and encodes the data as needed, before it is passed on to the attribute filter.
All parts of the resolver are always executed in a default configuration, even though much of the data gathered may be discarded later on in the attribute release/filtering stage. Because of this all configured data sources (such as LDAP Direcory Services or Relational Database Systems) should be highly available (and contain up-to-date, correct data, of course) – at least as highly available as you expect your IDP deployment to be. With LDAP directory servers achieving redundancy and replication usually is a much simpler (and cheaper) task than with RDBMSs, so sometimes it's worth the extra effort of synchronising data from other sources into an LDAP directory first , and only pointing the IDP to these LDAP data sources. But all of this depends on local Identity Management decisions and processes and no one recipe will fit all. Feel free to discuss pros and cons of approaches and tools on the eduID.at community mailing list.
Attribute resolver examples can be found in the We'll essentially replace the default /opt/shibboleth-idp/conf/
directory of a default Shibboleth IDPv3 installation: attribute-resolver.xml
, attribute-resolver-ldap.xml
and attribute-resolver-full.xml
. Only the file attribute-resolver.xml
is used by default, though, and we'll replace its content with our own definitions below, though keeping all the attribute ids that are now standardised (via the IDP's Attribute Registry feature, more on that below). Backup copies of all configuration files can always be found in /opt/shibboleth-idp/dist/conf/
for comparison and as source for copying/pasting of more/other definitions. In fact much of the content below is just collected from those example configuration files.
The attribute resolver contains two kinds of configuration itemsThe attribute resolver contains two kinds of configuration items: DataConnector
s, which supply input data from data sources as the LDAP or Database servers described above, and AttributeDefintion
s, which transform and encode the individual data elements (e.g. name, email address) retrieved from those DataConnectors into their . For the proper on-the-wire representation as SAML attributes . Every AttributeDefintion specifies how it should be represented on-the-wire with AttributeEncoder
elements, and its InputDataConnector
child element (s) reference from where which input data should come from (DataConnectors or other AttributeDefinitions) .
Table of Contents | ||
---|---|---|
|
XML root element
This is the XML "container" element all AttributeDefinitions and DataConnectors need to be wrapped in. Be sure to also properly close the root element with the final line </AttributeResolver>
as shown below:
or for other protocols) the IDP comes with a default set of transcoding rules referenced in /opt/shibboleth-idp/conf/attributes/default-rules.xml
. (Deployers of earlier versions of the software will notice how short and clean AttributeDefinitions can be, and the "missing" DisplayName and AttributeEncoder elements can all be found in the referenced transcoding rules, e.g. conf/attributes/inetOrgPerson.xml
, conf/attributes/eduPerson.xml
and so on.
Table of Contents | ||
---|---|---|
|
XML root element
This is the XML "container" element all AttributeDefinitions and DataConnectors need to be wrapped in. Be sure to also properly close the root element with the final line </AttributeResolver>
as shown below:
Code Block | ||||
---|---|---|---|---|
| ||||
<?xml version="1.0" encoding="UTF-8"?>
<AttributeResolver
xmlns | ||||
Code Block | ||||
| ||||
<?xml version="1.0" encoding="UTF-8"?> <AttributeResolver xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://shibboleth.net/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"> <!-- *everything* below needs to be included between the opening and closing tags of this AttributeResolver element --> </AttributeResolver> |
...
Tip |
---|
Most |
Info |
---|
Also, don't change the attribute |
If you have created attribute definitions that might also be relevant for other members of the If you have created attribute definitions that might also be relevant for other members of the community – e.g. based on the widely used (within Austria) Campus Online software – please share them! Contributions to this wiki are very much welcome. You can also just send them to the eduID.at Operations Team, of course, and we will edit and include them here.
...
Many SAML Service Providers will need the subject's real name in one form or another, e.g. to allow collaboration between subjects based on trusted, verified names. Some applications will only be able to use names in separate fields (first name, last name) and some are content with a unified field that contains the full name in a format chosen by the institution or the subject herself. So for interoperability every IDP will need to be able to provide both forms. Assuming you have the subject's name availabe available in an LDAP directory and configured a DataConnector for that (see further below), these are simple examples to use:
...
Code Block | ||
---|---|---|
| ||
<AttributeDefinition id="givenName" xsi:type="Simple">
<InputDataConnector ref="myLDAP" attributeNames="givenName" />
</AttributeDefinition> |
sn
Code Block | ||
---|---|---|
| ||
<AttributeDefinition <DisplayName xml:lang="de">Vorname</DisplayName>id="sn" xsi:type="Simple"> <DisplayName xml:lang="en">Given name</DisplayName> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:givenName" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.5.4.42" friendlyName="givenName" encodeType="false" /> </AttributeDefinition> |
sn
<InputDataConnector ref="myLDAP" attributeNames="sn" />
</AttributeDefinition> |
displayName
If you have the displayName
attribute available in your LDAP directory we'll go with that, as it's not generally being misused to store data other than the subject's real name (like "cn
" commonly is, e.g. storing the subject's login name or userid instead of her real name).
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="displayName | ||||
Code Block | ||||
| ||||
<AttributeDefinition id="surname" xsi:type="Simple"> <InputDataConnector ref="myLDAP" attributeNames="sndisplayName" /> <DisplayName xml:lang="de">Zuname</DisplayName> <DisplayName xml:lang="en">Surname</DisplayName> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:sn" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.5.4.4" friendlyName="sn" encodeType="false" /> </AttributeDefinition> |
displayName
</AttributeDefinition> |
If you only have the "cn" LDAP attribute available and it contains the subject's full name (not her login name / userid) you can simply change the InputDataConnector's attributeNames XML attribute to become attributeNames="cn"
in the second line of the example above. (Don't change anything else!) In this case you'll need to be aware that contrary to "cn" displayName is defined to be single-valued, though, and therefore you should be absolutely certain that your source attribute ("cn" in the modified example here) only ever contains a single value per subject in your LDAP deployment, for all subjects! If that requirement cannot bet met you will need to use a slightly more complex AttributeDefinition that takes one of the values (usually the first one as returned by an LDAP search) and turns that into the displayName attribute.
If you don't have the subject's full name available in your LDAP deployment (or your "cn" attribute is unusable because it may contain multiple values for a subject, but your givenName
and sn
attributes are single-valued) you can create this SAML attribute from first name and last name dynamically within the Shibboleth IDP:If you have the displayName
attribute available in your LDAP directory we'll go with that, as it's not generally being misused to store data other than the subject's real name (like "cn
" commonly is, e.g. storing the subject's login name or userid instead of her real name).
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="displayName" xsi:type="SimpleTemplate"> <InputDataConnector<InputAttributeDefinition ref="myLDAP" attributeNames="displayName"givenName" /> <DisplayName<InputAttributeDefinition xml:langref="desn">Name< /DisplayName>> <DisplayName xml:lang="en">Name</DisplayName> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:displayName" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.16.840.1.113730.3.1.241" friendlyName="displayName" encodeType="false" /> </AttributeDefinition> |
If you only have the "cn" LDAP attribute available and it contains the subject's full name (not her login name / userid) you can simply change the InputDataConnector's attributeNames XML attribute to become attributeNames="cn"
in the second line of the example above. (Don't change anything else!) In this case you'll need to be aware that contrary to "cn" displayName is defined to be single-valued, though, and therefore you should be absolutely certain that your source attribute ("cn" in the modified example here) also only ever contains a single value per subject in your LDAP deployment, for all subjects! If that requirement cannot bet met you will need to use a slightly more complex AttributeDefinition that takes one of the values (usually the first one as returned by an LDAP search) and turns that into the displayName attribute. You can request such an example to be added to this wiki on the eduid-discuss mailing list.
<Template>${givenName} ${sn}</Template>
</AttributeDefinition> |
Note that givenName
and sn
allow for multiple values, just like cn
! So only use the above method if you're certain that your LDAP directory is only using those attributes with single values.
Identifiers
With the notable exception of Library Services almost all (other) SAML Service Providers will need to consistently recognise a returning subject. I.e., they may not need to know who you are based on the identifier alone, but they will need to know that you are the same subject accessing their services as when you accessed it previously. That's what subjects expect themselfs, of course, otherwise all their work/data stored at a service would be unavailable to them the next time they accessed the same service! See also this comparison of commonly used identifiers and their properties from the Shibboleth wiki.
Info |
---|
There are a few types of identifier attributes in use within the federation community, each with their own unique properties, advantages and disadvantages. These are all explained in the Attributes section (or rather its child pages), for each attribute. So go read (and possibly re-read) those, as you will need to understand their definition and usage patterns in order to be able to decide if/how you can support them in your IDP. As always: Discuss with the community if anything is unclear! |
All eduID.at IDPs should be able to produce all of these identifiers, in order to be able to interoperate with – and make use of – all SAML Services Providers your community's members may need to work with.
If you have a "mail" attribute with the subject's email address available in your LDAP directory, the example below is all you need. Ideally the LDAP attribute should in practice be single-valued (i.e., never contain more than one attribute value for a subject) as some SAML Service Providers cannot handle multi-valued attributes. This attribute's specification does allow for multiple values, of course, but you may make your life harder by not being able to supply a single-valued version of what's in your LDAP directory.If you don't have the subject's full name available in your LDAP deployment (or your "cn" attribute is unusable because it may contain multiple values for a subject, but your givenName
and sn
attributes are single-valued) you can create this SAML attribute from first name and last name dynamically within the Shibboleth IDP:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition<AttributeDefinition id="displayNamemail" xsi:type="TemplateSimple"> <InputDataConnector ref="myLDAP" attributeNames="givenName snmail" /> <DisplayName xml:lang="de">Name</DisplayName> <DisplayName xml:lang="en">Name</DisplayName> <Template>${givenName} ${sn}</Template> <SourceAttribute>givenName</SourceAttribute> <SourceAttribute>sn</SourceAttribute> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:displayName" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:2.16.840.1.113730.3.1.241" friendlyName="displayName" encodeType="false" /> </AttributeDefinition> |
Note that givenName
and sn
allow for multiple values (just like cn
). So only use the above method if you're certain that your LDAP directory is only using them with single values.
Identifiers
With the notable exception of Library Services almost all (other) SAML Service Providers will need to consistently recognise a returning subject. I.e., they may not need to know who you are based on the identifier alone, but they will need to know that you are the same subject accessing their services as when you accessed them previously. That's what subjects expect themselfs, of course, otherwise all their work/data would be unavailable to them the next time they accessed the same service! See also this comparison of commonly used identifiers and their properties from the Shibboleth wiki.
Info |
---|
There are a few types of identifier attributes in use within the federation community, each with their own unique properties, advantages and disadvantages. These are all explained in the Attributes section (or rather its child pages), for each attribute. So go read (and possibly re-read) those, as you will need to understand their definition and usage patters to be able to decide if/how you can support them in your IDP. As always: Discuss with the community if anything is unclear! |
All eduID.at IDPs should be able to produce all of these identifiers, in order to be able to interoperate with – and make use of – all SAML Services Providers your community members may need to work with.
If you have a "mail" attribute with the subject's email address available in your LDAP directory, the example below is all you need. Ideally the LDAP attribute should in practice be single-valued (i.e., never contain more than one attribute value for a subject) as some SAML Service Providers cannot handle multi-valued attributes. This attribute's specification does allow for multiple values, of course, but you may make your life harder by not being able to supply a single-valued version of what's in your LDAP directory.
</AttributeDefinition> |
Some institutions may need more complex processing than the above, e.g. getting the value from one of several LDAP attribute depending on the role/affiliation of the subject (localPersonStudentMail, localPersonStafMail). The eduID.at community (or the eduID.at Operatons Team) will be able to supply you with other/more complex examples, so please ask.
Our documentation for the creation and usage of eduPersonPrincipalName also contains some info on using email addresses as identifiers (and why/when best to avoid it), but sometimes services just need an email address for email's sake and will rely on other attributes for the unique identification of the subject.
eduPersonPrincipalName
eduPersonPrincipalName is commonly produced either from the local login name (uid, sAMAccountName) or by re-using the institutional email address as its value. Our documentation for that attribute explains the pros and cons of each approach in detail. Below you'll find examples for both main methods used to create that attribute. First, the variant based on login name (replace sourceAttributeID="uid"
with sourceAttributeID="sAMAccountName"
or whatever holds local login names in your LDAP directory):
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- https://wiki.univie.ac.at/display/federation/eduPersonPrincipalName -->
<AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope} | ||||
Code Block | ||||
| ||||
<AttributeDefinition id="mail" xsi:type="Simple"> <InputDataConnector ref="myLDAP" attributeNames="mailuid" /> <DisplayName xml:lang="de">E-Mail-Adresse</DisplayName> <DisplayName xml:lang="en">Email address</DisplayName> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:mail" encodeType="false" /> <AttributeEncoder</AttributeDefinition> |
And here's a definition when you've chosen to re-use the institutional email address as eduPersonPrincipalName attribute value:
Note |
---|
Only do this if you are certain that all email address values for all subjects from your instituition are within (one of) your own institutional email domain(s). The eduPersonPrincipalName is filtered at SAML Service Providers to only allow domain values that have been allowed per each SAML Identity Provider, cf. the "attribute scope" column in the catalog of eduID.at Identity Providers. If needed you can ask the eduID.at operations team to allow more of your domains in your IDP's SAML Metadata, to match the email domains in use within your LDAP email attributes. But you cannot use email addresses as base for eduPersonPrincipalName attribute values if you populate external (arbitrary) email addresses in the referenced LDAP attribute. |
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="eduPersonPrincipalName" xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.3" friendlyName="mail" encodeType="falsePrescoped"> <InputDataConnector ref="myLDAP" attributeNames="mail" /> </AttributeDefinition> |
Some institutions may need more complex processing than the above, e.g. getting the value from one of several LDAP attribute depending on the role/affiliation of the subject (localPersonStudentMail, localPersonStafMail). The eduID.at community (or the eduID.at Operatons Team) will be able to supply you with other/more complex examples, so please ask.
Our documentation for creation and usage of eduPersonPrincipalName also contains some info on using email addresses as identifiers (and why/when best to avoid it), but sometimes services just need an email address for email's sake and will rely on other attributes for the unique identification of the subject.
eduPersonPrincipalName
eduPersonPrincipalName is commonly produced either from the local login name (uid, sAMAccountName) or by re-using a copy of the institutional email address as its value. Our documentation for that attribute explains the pros and cons of each approach in detail. Below you'll find examples for both main methods used to create that attribute. First, the variant based on login name (replace sourceAttributeID="uid"
with sourceAttributeID="sAMAccountName"
or whatever holds local login names in your LDAP directory):
A more complex example could use a ScriptedAttribute type definition to enforce in code that only email addresses matching local mail domains will be used, but what to do about those addresses that fail the check? So in such cases it's probably best to chose some other strategy to create eduPersonPrincipalName values.
SAML SubjectID
The SAML SubjectID can be seen as an opaque (not name-based, long "ugly" values), more stable version of eduPersonPrincipalName. It is intended as a replacement for the eduPersonUniqueID attribute and possibly also for eduPersonPrincipalName itself. The example provided below re-uses configuration already made to support persistent NameIDs, namely the properties idp.persistentId.sourceAttribute
(from /opt/shibboleth-idp/conf/saml-nameid.properties
) and idp.persistentId.salt
(from /opt/shibboleth-idp/credentials/secrets.properties
).
Provided you already have a stable, non-recycled (i.e., not reassigned from one subject to another) internal identifier for your subjects you can set that attribute in the idp.persistentId.sourceAttribute
property of the aforementioned config file and it will also be used as the basis for the SubjectID attribute. The configuration below also re-uses the salt configured in the property idp.persistentId.salt
to generate a salted hash of the chosen source attribute as (local part of the) SubjectID attribute value:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="subjectHash" xsi:type="ScriptedAttribute" dependencyOnly="true" | ||||
Code Block | ||||
| ||||
<!-- https://wiki.univie.ac.at/display/federation/eduPersonPrincipalName --> <AttributeDefinition id="eduPersonPrincipalName" xsi:type="Scoped" scope="%{idp.scope}"> <InputDataConnector ref="myLDAP" attributeNames="uid%{idp.persistentId.sourceAttribute}" /> <DisplayName xml:lang="de">Benutzerkennung</DisplayName> <Script><![CDATA[ var <DisplayNamedigestUtils xml:lang="en">User identifier</DisplayName>= Java.type("org.apache.commons.codec.digest.DigestUtils"); <AttributeEncoder xsi:type="SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" /> <AttributeEncoder var saltedHash = digestUtils.sha256Hex(%{idp.persistentId.sourceAttribute}.getValues().get(0) + "%{idp.persistentId.salt}"); subjectHash.addValue(saltedHash); ]]></Script> </AttributeDefinition> <AttributeDefinition id="samlSubjectID" xsi:type="SAML2ScopedStringScoped" namescope="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false%{idp.scope}"> <InputAttributeDefinition ref="subjectHash" /> </AttributeDefinition> |
And here's a definition when you've chosen to re-use the institutional email address as eduPersonPrincipalName attribute value:
Note |
---|
Only do this if you are certain that all email address values for all subjects from your instituition are within (one of) your own institutional email domain(s). The eduPersonPrincipalName is filtered at SAML Service Providers to only allow domain values that have been whitelisted per each SAML Identity Provider, cf. the "attribute scope" column in the catalog of eduID.at Identity Providers. If needed you can ask the eduID.at operations team to whitelist more of your domains in your IDP's SAML Metadata, to match the email domains in use within your LDAP email attributes. But you cannot use email addresses as base for eduPersonPrincipalName attribute values if you populate external (arbitrary) email addresses in the referenced LDAP attribute. |
If you do not have such an identifier readily available but you can fabricate one based on other/more data that would be an alternative approach. For example, if login names may be reassigned at your organisation – meaning you cannot base SubjectID solely on login names, salted/hashed or not – you could concatenate the login name with something that's not going to be the same in the re-assigned account to create an interim attribute within the IDP that then becomes the basis for other attributes and NameIDs:
For example if accounts get a new account creation date after re-activation (i.e., when you first delete and then later re-create accounts) you should also be able to use a combination of loginname+accountcreationdate
or something along those lines, which would then still be unique even if the same login name would later be reused in a new account for another – or even for the same, which may be an unintended but still acceptable side-effect – person. So first you'd pull the data to combine into an interim attribute defintion, e.g.:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="subjectIdBasis" xsi:type="Template" dependencyOnly="true | ||||
Code Block | ||||
| ||||
<AttributeDefinition id="eduPersonPrincipalName" xsi:type="Prescoped"> <InputDataConnector ref="myLDAP" attributeNames="mailuserPrincipalName whenCreated" /> <DisplayName xml:lang="de">Benutzerkennung</DisplayName> <DisplayName xml:lang="en">User identifier</DisplayName> <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> |
A more complex example could use a ScriptedAttribute type definition to enforce in code that only email addresses matching local mail domains will be used, but what to do about those addresses that fail the check? So in such cases it's probably best to chose some other strategy to create eduPersonPrincipalName values.
SAML SubjectID
The SAML SubjectID can be seen as an opaque (not name-based, long "ugly" values), more stable version of eduPersonPrincipalName. It is intended as a replacement for the eduPersonUniqueID attribute, possibly also for eduPersonPrincipalName. The example provided below re-uses configuration already made to support persistent NameIDs, namely the settings idp.persistentId.sourceAttribute
and idp.persistentId.salt
from the file /opt/shibboleth-idp/conf/saml-nameid.properties
.
Provided you already have a stable, non-recycled (not reassigned from one subject to another) internal identifier for your subjects you can set that attribute in the idp.persistentId.sourceAttribute
property of the referenced config file, and it will also be used as the basis for the SubjectID attribute. The configuration below also re-uses the salt configured in the property idp.persistentId.salt
to generate a salted hash of the chosen source attribute as (local part of the) SubjectID attribute value:
<Template>${userPrincipalName} ${whenCreated}</Template>
</AttributeDefinition> |
Then you put this attribute's id into your saml-nameid.properties
configuration file so that this property can then be used in the rest of the configuration (same way for everyone, independently of the specifics):
No Format |
---|
idp.persistentId.sourceAttribute = subjectIdBasis |
Finally you'd have to change only one line from the above example of creating SubjectIDs, replacing the InputDataConnector
on the "subjectHash
" attribute definition with the following InputAttributeDefinition
:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="subjectHash" xsi:type="ScriptedAttribute" dependencyOnly="true">
- <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
+ <InputAttributeDefinition ref="subjectIdBasis" />
<Script><![CDATA[ |
Now the "subjectHash
" attribute definition will use the "fabricated" identifier (combined from the userPrincipalName
and whenCreated
attributes) as basis and the rest of that example above works the same way for everyone (including the samlSubjectID
attribute defintion which does not need to be changed at all).
Note |
---|
This configuration generates |
SAML PairwiseID
The SAML PairwiseID is an opaque, persistent, service-specific pseudonym. It replaces the eduPersonTargetedID attribute as well as SAML 2.0 persistent NameIDs. The example provided below re-uses configuration already made to support persistent NameIDs, namely the properties idp.persistentId.sourceAttribute
(from /opt/shibboleth-idp/conf/saml-nameid.properties
) and idp.persistentId.salt
(from /opt/shibboleth-idp/credentials/secrets.properties
).
Code Block | ||||
---|---|---|---|---|
| ||||
Code Block | ||||
| ||||
<AttributeDefinition id="subjectHashsamlPairwiseID" xsi:type="ScriptedAttributeScoped" dependencyOnlyscope="true%{idp.scope}"> <InputDataConnector ref="myLDAPcomputed" attributeNames="%{idp.persistentId.sourceAttribute}computedId" /> <Script><![CDATA[ var digestUtils = Java.type("org.apache.commons.codec.digest.DigestUtils"); var saltedHash = digestUtils.sha256Hex(%{idp.persistentId.sourceAttribute}.getValues().get(0) + "%{idp.persistentId.salt}"); subjectHash.addValue(saltedHash); ]]></Script> </AttributeDefinition> <AttributeDefinition id="subject-id" xsi:type="Scoped" scope="%{idp.scope}"> <InputAttributeDefinition ref="subjectHash" /> <DisplayName xml:lang="de">Opake Benutzerkennung</DisplayName> <DisplayName xml:lang="en">Opaque user identifier</DisplayName> <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oasis:names:tc:SAML:attribute:subject-id" friendlyName="subject-id" encodeType="false</AttributeDefinition> |
This references a DataConnector with id="computed" which you'll create using the next snippet (move it to the end of the attribute-resolver.xml file so it ends up next to the other DataConnector elements). Provided you already have a stable, non-recycled (not reassigned from one subject to another) identifier for your subjects stored in LDAP you can set that attribute name in the idp.persistentId.sourceAttribute
property of the referenced config file and it will also be used as the basis for the PairwiseID attribute. The configuration below also re-uses the salt configured in the property idp.persistentId.salt
to generate a salted hash of the chosen source attribute as (local part of the) PairwiseID attribute value:
Code Block | ||||
---|---|---|---|---|
| ||||
<DataConnector id="computed" xsi:type="ComputedId" excludeResolutionPhases="c14n/attribute" generatedAttributeID="computedId" salt="%{idp.persistentId.salt}" algorithm="%{idp.persistentId.algorithm:SHA}" encoding="%{idp.persistentId.encoding:BASE32}"> <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" /> </AttributeDefinition>DataConnector> |
Similarly to SubjectIDs above: If you do not have one such an identifier readily available but you can fabricate one based on other/more data that would be an alternative approach. For example, if login names may be reassigned at your organisation – meaning you cannot base SubjectID solely on login names, salted/hashed or not – you could concatenate the login name with something that's not going to be the same in the re-assigned account to create an interim attribute within the IDP that then becomes the basis for other attributes and NameIDs:
For example if accounts get a new account creation date after re-activation (i.e., when you first delete and then later re-create accounts) you should also be able to use a combination of loginname+accountcreationdate
or something along those lines, which would then still be unique even if the same login name would later be reused in a new account for another – or even for the same, which may be an unintended but still acceptable side-effect – person. So first you'd pull the data to combine into the interim attribute defintion, e.g.:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="subjectIdBasis" xsi:type="Template">
<InputDataConnector ref="myLDAP" attributeNames="userPrincipalName whenCreated" />
<Template>${userPrincipalName} ${whenCreated}</Template>
</AttributeDefinition> |
Then you put this attribute's id into your saml-nameid.properties
configuration file so that this property can then be used in the rest of the configuration (same way for everyone, independently of the specifics):
No Format |
---|
idp.persistentId.sourceAttribute = subjectIdBasis |
Finally you'd have to change only one line from the above example of creating SubjectIDs, replacing the InputDataConnector
on the "subjectHash
" attribute definition with the following InputAttributeDefinition
:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="subjectHash" xsi:type="ScriptedAttribute" dependencyOnly="true">
- <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
+ <InputAttributeDefinition ref="subjectIdBasis" />
<Script><![CDATA[ |
Now the "subjectHash
" attribute definition will use the "fabricated" identifier (combined from the userPrincipalName
and whenCreated
attributes) as basis and the rest of that example above works the same way for everyone (including the "subject-id
" attribute defintion which does not need to be changed at all).
Note |
---|
This configuration generates |
SAML PairwiseID
The SAML PairwiseID is an opaque, persistent, service-specific pseudonym. It is intended as a replacement for the eduPersonTargetedID attribute as well as for standard SAML 2.0 persistent NameIDs. The example provided below re-uses configuration already made to support persistent NameIDs, namely the settings idp.persistentId.sourceAttribute
and idp.persistentId.salt
from the file /opt/shibboleth-idp/conf/saml-nameid.properties
.
don't have a stable, non-reassigned internal identifier in your Systems of Record (LDAP directory, relational database) and decided to fabricate one (as shown in the examples for SubjectID above) you'll need to replace that DataConnector's dependency with the custom one you created earlier, e.g.:
Code Block | ||||
---|---|---|---|---|
| ||||
- <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
+ <InputAttributeDefinition ref="subjectIdBasis" /> |
Note |
---|
This simple configuration generates Using a more complex configuration with StoredID instead of ComputedID data connectors and a StorageService it is possible to override or map chosen input attributes to previously generated output attributes. That would allow to keep the generated and released identifiers ( |
European Student Identifier
All Higher Education Institutions will want to make available the European Student Identifier (ESI) as that's one of the required attributes in order to access Erasmus+ services.
Warning |
---|
The examples in this section assume use of the Shibboleth IDPv4 software without its new Attribute Registry and so will work as is for IDP systems that have been upgraded from IDPv3. It does so by including (While this whole documentation set is geared towards fresh installations of IDPv4 it seems more useful to provide instructions that require part of the deployers to remove individual lines from the examples rather than expecting them to find out what and where to add something. This assessment may change and the direct applicability of the examples "reversed" in the future, though.) |
Since the ESI won't be available in the exact format required by the ESI specification we'll provide examples that dynamically generate the ESI based on the uid
attribute (coming from the id="myLDAP"
DataConnector). In this example the uid
attribute is expected to have values of the form "x<MATRIKELNR>
" where <MATRIKELNR>
is the student's Austrian immatriculation number so that we can extract a useable form of the immatriculation number from the uid attribute using regular expressions. Any other values not matching the given pattern will lead to an empty ESI attribute which will ultimetely not be released by the IDP:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="schacPersonalUniqueCode" xsi:type="Mapped">
<InputDataConnector ref="myLDAP" attributeNames="uid | ||||
Code Block | ||||
| ||||
<AttributeDefinition id="pairwise-id" xsi:type="Scoped" scope="%{idp.scope}"> <InputDataConnector ref="computed" attributeNames="ComputedID" /> <DisplayName xml:lang="de">Service-spezifische Benutzerkennung<>Europäische Studierendenkennung (ESI)</DisplayName> <DisplayName xml:lang="en">Service-specific pseudonym</DisplayName>>European Student Identifier (ESI)</DisplayName> <ValueMap> <AttributeEncoder xsi:type="SAML2ScopedString" name="urn:oasis:names:tc:SAML:attribute:pairwise-id" friendlyName="pairwise-id" encodeType="false" /> </AttributeDefinition> |
This references a DataConnector with id="computed" which you'll create using the next snippet (move it to the end of the attribute-resolver.xml file together with the other data connectors). Provided you already have a stable, non-recycled (not reassigned from one subject to another) identifier for your subjects stored in LDAP you can set that attribute name in the idp.persistentId.sourceAttribute
property of the referenced config file and it will also be used as the basis for the PairwiseID attribute. The configuration below also re-uses the salt configured in the property idp.persistentId.salt
to generate a salted hash of the chosen source attribute as (local part of the) PairwiseID attribute value:
<ReturnValue>urn:schac:personalUniqueCode:int:esi:at:$1</ReturnValue>
<SourceValue>^x([0-9]{8,})$</SourceValue>
</ValueMap>
<AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.25178.1.2.14" friendlyName="schacPersonalUniqueCode" encodeType="false" />
</AttributeDefinition> |
Don't forget to adapt the pattern specified in the SourceValue element above since you won't be finding the immatriculation number to be stored within your own systems in exactly the format shown above.
Info | ||
---|---|---|
| ||
Instead of |
In case you have the immatriculation number available in its own attribute already (i.e., no regular expression matching is needed to extract it from other data; we'll assume use of the ficticious matrikelnummer
attribute below) you could use a "Template" attribute definition instead of the "Mapped" one above:
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="schacPersonalUniqueCode | ||||
Code Block | ||||
| ||||
<DataConnector id="computed" xsi:type="ComputedId" generatedAttributeID="ComputedID"Template"> <InputDataConnector ref="myLDAP" attributeNames="matrikelnummer" /> salt="%{idp.persistentId.salt}" algorithm="%{idp.persistentId.algorithm:SHA}"<DisplayName xml:lang="de">Europäische Studierendenkennung (ESI)</DisplayName> <DisplayName xml:lang="en">European Student Identifier (ESI)</DisplayName> encoding="%{idp.persistentId.encoding:BASE32}"><Template>urn:schac:personalUniqueCode:int:esi:at:${matrikelnummer}</Template> <InputDataConnector<AttributeEncoder refxsi:type="myLDAPSAML2String" attributeNames="%{idp.persistentId.sourceAttribute}name="urn:oid:1.3.6.1.4.1.25178.1.2.14" friendlyName="schacPersonalUniqueCode" encodeType="false" /> </DataConnector> |
Similarly to SubjectIDs above: If you don't have a stable, non-reassigned internal identifier in your Systems of Record (LDAP directory, relational database) and decided to fabricate one as – as shown in the examples for SubjectID above – you'll need to replace that DataConnector's dependency with the custom one you created earlier, e.g.:
Code Block | ||||
---|---|---|---|---|
| ||||
- <InputDataConnector ref="myLDAP" attributeNames="%{idp.persistentId.sourceAttribute}" />
+ <InputAttributeDefinition ref="subjectIdBasis" /> |
...
AttributeDefinition> |
Finally, institutions that do not manage immatriculation numbers for their students but that still do need to provide the ESI attribute can use the following example to dynamically generate ESI values from any other locally available identifier (below again assuming use of the uid
attribute) combining it with the canonical DNS Domain ("scope") of the institution:
Code Block | ||
---|---|---|
| ||
<AttributeDefinition id="schacPersonalUniqueCode" xsi:type="Template">
<InputDataConnector ref="myLDAP" attributeNames="uid" />
<DisplayName xml:lang="de">Europäische Studierendenkennung (ESI)</DisplayName>
<DisplayName xml:lang="en">European Student Identifier (ESI)</DisplayName>
<Template>urn:schac:personalUniqueCode:int:esi:%{idp.scope}:${uid}</Template>
<AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.25178.1.2.14" friendlyName="schacPersonalUniqueCode" encodeType="false" />
</AttributeDefinition> |
See the local (i.e., Austrian) profile of the European Student Identifier specification for the abstract requirements.
This simple configuration generates pairwise-id
attribute values dynamically based on the configured input attribute on each login, and without any way to manually influence or override this in selected cases. So if the configured (or fabricated) internal identifier you derive those attribute values from should ever change for a given person so will all her pairwise-id
attribute values for the services that receive them.
...
Authorization / Org data
eduPersonScopedAffiliation
eduPersonScopedAffiliation is sometimes used for simple authorisation cases. eduPersonAffiliation describes a person's relationship with the IDP's organisation in general terms (from a controlled vocabulary of only 8 allowed values), and eduPersonScopedAffiliation is simply the scoped variant of that (i.e., the applicable affiliation values each suffixed with "@" + the main institutional domain, same as for eduPersonPrincipalName or eduPersonUniqueIDsamlSubjectID). Only the scoped version should be used for federated use cases: Even if a recieving Service Provider did not want need to differentiate between e.g. staff@example-unifaculty@example.atedu
and staff@researchfaculty@research.example.com
it can always easily throw away the scope with minimal processing, yielding the unscoped version (here: "staff
faculty
" in both cases).
In the examples below we'll first create the unscoped version using one of several alternative methods, since this part will need be done differently at most organisations as it depends on local Identity Management choices. Then further below we'll create the scoped variant from that, in a configuration snippet that can be remains the same for everyone, no matter how the (unscoped) affiliation values was created.
Here's a first very simple example that creates a few of the affiliation values based on a regex match of some other attribute's value, in this case the login name as stored in the "uid" LDAP attribute (cf. sourceAttributeID
). Replace with "sAMAccountName" or "cn" or whatever as needed in your deployment. This method can be used if you assign login names based on a schema that encodes the role/affiliation of a subject into her login name/userid. While overloading identifiers with semantics (such as role information) is not generally recommended such practices exist , so you might as well make use of them if it makes your IDP configuration easier. Note that identical SouceValue
SourceValue
elements are used twice below to make sure all students are also members, and all staff are also members, too, as required by the eduPerson specification.
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="eduPersonAffiliation" xsi:type="Mapped" dependencyOnly="true"> <InputDataConnector ref="myLDAP" attributeNames="uid" /> <ValueMap> <ReturnValue>student</ReturnValue> <SourceValue>m.+</SourceValue> </ValueMap> <ValueMap> <ReturnValue>staff</ReturnValue> <SourceValue>p.+</SourceValue> </ValueMap> <ValueMap> <ReturnValue>member</ReturnValue> <SourceValue>m.+</SourceValue> <SourceValue>p.+</SourceValue> </ValueMap> </AttributeDefinition> |
Now hereHere's another example where the correct affiliations are derived from the name of a locally defined group attribute (phoUsergroup
in the example below). This example uses partial string matches for the individual affiliations, and a regex that makes all subjects with any group value (at least, or also) a member
:
...
Tip | ||
---|---|---|
There are of course many more ways this could be done, depending on local data available and LDAP deployment decisions (e.g. group implementation). The wiki for the old Shibboleth IDP v2.x software has more and more complex examples, including one to recursively map affiliations from nested groups within Microsoft "Active Directory" deployments.
|
Finally, it's time to turn the (unscoped) eduPersonAffiliation values created by one of the methods above into a properly scoped one:
Code Block | ||
---|---|---|
| ||
<AttributeDefinition id="eduPersonScopedAffiliation"<AttributeDefinition id="eduPersonScopedAffiliation" xsi:type="Scoped" scope="%{idp.scope}"> <InputAttributeDefinition ref="eduPersonAffiliation" /> <DisplayName xml:lang="de">Rollen an der Institution</DisplayName> <DisplayName xml:lang="en">Affiliations</DisplayName> <AttributeEncoder xsi:type="SAML1ScopedStringScoped" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" encodeType="false" /scope="%{idp.scope}"> <AttributeEncoder<InputAttributeDefinition xsi:type="SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" encodeType="falseref="eduPersonAffiliation" /> </AttributeDefinition> |
...
eduPersonEntitlement is a container for all kinds of values and data, usually only with clearly defined values within closed specific communities. One notable exception is its global use for location-independent, off-campus authorisation to Library Services. Here's a simple example that should work for most deployments, giving all subjects you have declared to have an eduPersonAffilliation
of member
earlier (see above) or of library-walk-in
(someone physically present in library premises) the common-lib-terms
entitlement, thereby stating that these all are entitled to access licensed resources on behalf of your organisation, according to the "common library licensing terms" (again, see Library Services for details):
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="eduPersonEntitlement" xsi:type="Mapped"> <InputAttributeDefinition ref="eduPersonAffiliation" /> <DisplayName xml:lang="de">Berechtigungen</DisplayName> <DisplayName xml:lang="en">Entitlements</DisplayName> <ValueMap> <ReturnValue>urn:mace:dir:entitlement:common-lib-terms</ReturnValue> <SourceValue>member</SourceValue> <SourceValue>library-walk-in</SourceValue> </ValueMap> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonEntitlement" encodeType="false" /> <SourceValue>member</SourceValue> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" friendlyName="eduPersonEntitlement" encodeType="false" /> <SourceValue>library-walk-in</SourceValue> </ValueMap> </AttributeDefinition> |
Another use-case relevant to the ACOnet and GÉANT communities is the GÉANT Trusted Certificate Service that relies on a specific eduPersonEntitlement
value to signal that a given subject satisfies the criteria to automatically issue them personal X.509 certificates (based on personal data provided in other SAML attributes, such as name and email address).
...
Code Block | ||||
---|---|---|---|---|
| ||||
<AttributeDefinition id="eduPersonEntitlement" xsi:type="Mapped"> <InputAttributeDefinition ref="eduPersonAffiliation" /> <DisplayName xml:lang="de">Berechtigungen</DisplayName> <DisplayName xml:lang="en">Entitlements</DisplayName> <ValueMap> <ReturnValue>urn:mace:dir:entitlement:common-lib-terms</ReturnValue> <SourceValue>member</SourceValue> <SourceValue>library-walk-in</SourceValue> </ValueMap> <ValueMap> <ReturnValue>urn:mace:terena.org:tcs:personal-user</ReturnValue> <SourceValue>faculty</SourceValue> </ValueMap> <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonEntitlement" encodeType="false" /> <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" friendlyName="eduPersonEntitlement" encodeType="false" /> </ValueMap> </AttributeDefinition> |
You can have several SourceValue
elements in a ValueMap, e.g. to additionally allow everyone with affiliation student
to get a personal certificate as well. See the examples above (eduPersonScopedAffiliation) or the Shibboleth wiki for details.
Tip |
---|
If you're supporting use of your Shibboleth IDP to access USI Wien services check out another variant to create |
schacHomeOrganization
schacHomeOrganization is sometimes needed by services, usually as an IDP- and entityID
-independent identifier for an organization, e.g. to map subjects from an IDP to a contract in the name of the organisation that runs the IDP (without having to hard-code the IDP's entityID into some configurationn file or database). The following will work for anyone, based on the data connector provided below (that's also generic, thanks to its use of Java properties)::
Code Block | ||
---|---|---|
| ||
<!-- https://wiki.univie.ac.at/display/federation/schacHomeOrganization -->
<AttributeDefinition id="schacHomeOrganization" | ||
Code Block | ||
| ||
<!-- https://wiki.univie.ac.at/display/federation/schacHomeOrganization --> <AttributeDefinition id="schacHomeOrganization" xsi:type="Simple"> <InputDataConnector ref="staticAttributes" attributeNames="schacHomeOrganization" /> <DisplayName xml:lang="de">Institutionskürzel</DisplayName> <DisplayName xml:lang="en">Institution id</DisplayName> <AttributeEncoder xsi:type="SAML1String" name="urn:oid:1.3.6.1.4.1.25178.1.2.9" encodeType="false" /> <AttributeEncoder xsi:type="SAML2StringSimple" name="urn:oid:1.3.6.1.4.1.25178.1.2.9" friendlyName="schacHomeOrganization" encodeType="false> <InputDataConnector ref="staticAttributes" attributeNames="schacHomeOrganization" /> </AttributeDefinition> |
...
And finally here's a verbatim copy of the default example of an LDAP DataConnector (taken from the file attribute-resolver-ldap.xml
). All the parameters and values in this DataConnector come from the conf/ldap.properties
file in the IDP's conf directoryor from credentials/secrets.properties
, so nothing needs to be set/changed here – with below – with the exception of possibly removing some removing some of the XML attributes. : E.g. for LDAP directory server deployments deployments without any transport-layer security (no TLS and no SSL) you'd need to remove the trustFile
XML-attribute (i.e., the whole line starting with trustFile
), the . The rest should still work for everyone, based on the (correct, or default) settings in ldap.properties
:
Code Block | ||||
---|---|---|---|---|
| ||||
<DataConnector id="myLDAP" xsi:type="LDAPDirectory" ldapURL="%{idp.attribute.resolver.LDAP.ldapURL}" baseDN="%{idp.attribute.resolver.LDAP.baseDN}" principal="%{idp.attribute.resolver.LDAP.bindDN}" principalCredential="%{idp.attribute.resolver.LDAP.bindDNCredential}" useStartTLS="%{idp.attribute.resolver.LDAP.useStartTLS:true}" connectTimeout="%{idp.attribute.resolver.LDAP.connectTimeout}" trustFile="%{idp.attribute.resolver.LDAP.connectTimeouttrustCertificates}" trustFileresponseTimeout="%{idp.attribute.resolver.LDAP.trustCertificatesresponseTimeout}" responseTimeoutconnectionStrategy="%{idp.attribute.resolver.LDAP.responseTimeout}"connectionStrategy}" noResultIsError="true" multipleResultsIsError="true" excludeResolutionPhases="c14n/attribute"> <FilterTemplate> <![CDATA[ %{idp.attribute.resolver.LDAP.searchFilter} ]]> </FilterTemplate> <ConnectionPool minPoolSize="%{idp.pool.LDAP.minSize:3}" maxPoolSize="%{idp.pool.LDAP.maxSize:10}" blockWaitTime="%{idp.pool.LDAP.blockWaitTime:PT3S}" validatePeriodically="%{idp.pool.LDAP.validatePeriodically:true}" validateTimerPeriod="%{idp.pool.LDAP.validatePeriod:PT5M}" expirationTimevalidateDN="%{idp.pool.LDAP.idleTimevalidateDN:PT10M}" failFastInitializevalidateFilter="%{idp.pool.LDAP.failFastInitialize:falsevalidateFilter:(objectClass=*)}" expirationTime="%{idp.pool.LDAP.idleTime:PT10M}"/> </DataConnector> |
If you're done with editing activate the changes by restarting Tomcat – assuming you've changed some Java property files (such as saml-nameid.properties)
which are only read on startup of the JVM:
No Format |
---|
systemctl restart tomcat9 |
Otherwise (and at At any later point, once the IDP has all the properties set) , you can/ should activate resolver changes in a running IDP by reload only the IDP's attribute resolver sub-system (not by restarting the IDP or Tomcat):
No Format |
---|
/opt/shibboleth-idp/bin/reload-service.sh -id shibboleth.AttributeResolverService |
...