Shibboleth IdP v3 Attribute Resolver Configuration

These are a few examples of how some of the needed attributes for use with CCC-wide services and cloud services might be defined, depending on your attribute source(s), in the "attribute-resolver.xml" file. Exactly what the underlying attribute you'd use in your directory for some of this might vary, depending on the choices you make for how to construct eduPersonPrincipalName, or how you generate values for eduPersonAffiliation, etc. There are aso a couple of attached "full examples" of an IdP v3 attribute-resovler.xml file, one that you might have if you had two distinct attribute sources for staff versus students, and the other where you have a single directory for both. You still would  need to make changes, and the "2-source" example depends on using the Unicon SplitAuthn extension to Shibboleth IdP v3.

edPersonPrincipalName

For Active Directory, it is most common to use sAMAccountName as the identifier. Note that the scope="%{idp.scope}"  part of the config refers to a property set in the main 'conf/idp.properties' file, with a value like 'college.edu'. If you have only a single directory that covers both staff and students, you'd only have one <resolver:Dependency ref="myLDAP"> in the definition.

	<resolver:AttributeDefinition xsi:type="ad:Scoped" id="eduPersonPrincipalName" scope="%{idp.scope}" sourceAttributeID="sAMAccountName">
	    <resolver:Dependency ref="employeeLDAP" />
	    <resolver:Dependency ref="studentLDAP" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
	</resolver:AttributeDefinition>

But let's say you have do have a separate staff and student directory, and want to use sAMAccountName for staff, but that you store the student id for a student in, for example,  employeeNumber, you could have the following:

	<resolver:AttributeDefinition xsi:type="ad:Scoped" id="eduPersonPrincipalName" scope="%{idp.scope}" sourceAttributeID="sAMAccountName">
	    <resolver:Dependency ref="employeeLDAP" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
	</resolver:AttributeDefinition>
 
	<resolver:AttributeDefinition xsi:type="ad:Scoped" id="eduPersonPrincipalName" scope="%{idp.scope}" sourceAttributeID="employeeNumber">
	    <resolver:Dependency ref="studentLDAP" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonPrincipalName" encodeType="false" />
	    <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" friendlyName="eduPersonPrincipalName" encodeType="false" />
	</resolver:AttributeDefinition>

edPersonAffiliation and eduPersonPrimaryAffiliation

If you have distinct directories for  staff and students, then the easiest way to "generate" the right values for edPersonAffilation and eduPersonPrimaryAffiliation are to "hard-code" them as this following example shows. (This also shows setting the values for eduPersonScopedAffiliation, which should be the same set of values as edPersonAffiliation, only with the same scope added to each value as was added above to create the EPPN value.)

    <resolver:AttributeDefinition xsi:type="ad:Simple" id="eduPersonAffiliation" sourceAttributeID="eduPersonAffiliation">
        <resolver:Dependency ref="staticEmployeeAttributes" />
        <resolver:Dependency ref="staticStudentAttributes" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:eduPersonAffiliation" encodeType="false" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" friendlyName="eduPersonAffiliation" encodeType="false" />
    </resolver:AttributeDefinition>

    <resolver:AttributeDefinition xsi:type="ad:Scoped" id="eduPersonScopedAffiliation" scope="%{idp.scope}" sourceAttributeID="eduPersonAffiliation">
        <resolver:Dependency ref="staticEmployeeAttributes" />
        <resolver:Dependency ref="staticStudentAttributes" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" encodeType="false" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" encodeType="false" />
    </resolver:AttributeDefinition>

    <resolver:AttributeDefinition xsi:type="ad:Simple" id="eduPersonPrimaryAffiliation" sourceAttributeID="eduPersonPrimaryAffiliation">
        <resolver:Dependency ref="staticEmployeeAttributes" />
        <resolver:Dependency ref="staticStudentAttributes" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation" encodeType="false" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.5" friendlyName="eduPersonPrimaryAffiliation" encodeType="false" />
 
....
 
    <!-- Employee Static attributes for eduPerson*Affiliation -->
    <resolver:DataConnector id="staticEmployeeAttributes" xsi:type="dc:Static"
		activationConditionRef="shibboleth.EmployeeAuthSourceActivationCondition">
        <dc:Attribute id="eduPersonAffiliation">
            <dc:Value>member</dc:Value>
            <dc:Value>staff</dc:Value>
			<dc:Value>employee</dc:Value>
        </dc:Attribute>
        <dc:Attribute id="eduPersonPrimaryAffiliation">
            <dc:Value>staff</dc:Value>
        </dc:Attribute>
    </resolver:DataConnector>
 
    <!-- Student Static attributes for eduPerson*Affiliation -->
    <resolver:DataConnector id="staticStudentAttributes" xsi:type="dc:Static"
		activationConditionRef="shibboleth.StudentAuthSourceActivationCondition">
        <dc:Attribute id="eduPersonAffiliation">
            <dc:Value>member</dc:Value>
            <dc:Value>student</dc:Value>
        </dc:Attribute>
        <dc:Attribute id="eduPersonPrimaryAffiliation">
            <dc:Value>student</dc:Value>
        </dc:Attribute>
    </resolver:DataConnector>
 

 

However, if you have a single directory for both staff and students, and you don't have any attribute in that directory that explicitly identifies the "role" of the person, if they are staff or student or both, then you generally need to have "code", a script, that generates the appropriate values for eduPersonAffiliation etc.  That might be group memberships, or based on the OU branch within the directory that the user object is contained within. And you have to decide, for those folks who are both staff and student, which "role" takes precedence, which role should be listed as the person's eduPersonPrimaryAffiliation. In the below, this example "code" generates affiliation values based on group memberships in AD, and the 'staff' role takes precedence. Clearly this is just an example, you'd need to modify this script to match your directory, group structure, etc. (This example is for Shibboleth IdPv3, it won't work in v2, as the underlying "code engine" is different,)

<resolver:AttributeDefinition xsi:type="ad:Simple" xmlns="urn:mace:shibboleth:2.0:resolver:ad" id="memberOf" sourceAttributeID="memberOf">
		<resolver:Dependency ref="campusActiveDirectory" />
		<resolver:AttributeEncoder xsi:type="enc:SAML1String" xmlns="urn:mace:shibboleth:2.0:attribute:encoder" name="urn:mace:dir:attribute-def:memberOf" />
 
    <resolver:AttributeDefinition id="eduPersonAffiliation" xsi:type="ad:Script" xmlns="urn:mace:shibboleth:2.0:resolver:ad">
        <resolver:Dependency ref="memberOf" /> 
        <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:eduPersonAffiliation" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" friendlyName="eduPersonAffiliation" />
        <Script><![CDATA[
        var LoggerFactory = Java.type("org.slf4j.LoggerFactory");
        logger = LoggerFactory.getLogger("edu.internet2.middleware.shibboleth.resolver.script.affiliation");

        eduPersonAffiliation.getValues().clear();

        logger.debug("memberOf is: " + memberOf.getValues());

        // Add at least one value
        eduPersonAffiliation.addValue("member");

        // If the user has group membership
        if (typeof memberOf != "undefined" && memberOf != null ){
            // The go through each group membership and add the appropriate affiliation
            // The IdP will remove duplicate values so we don't need to worry about that here
            for ( i = 0; memberOf != null && i < memberOf.getValues().size(); i++ ){
                value = memberOf.getValues().get(i).toString().toUpperCase();

                if (value.indexOf("STUDENTS") >= 0){
                    eduPersonAffiliation.addValue("student");
                }

                if (value.indexOf("EMPLOYEES") >= 0){
                    eduPersonAffiliation.addValue("staff");
                    eduPersonAffiliation.addValue("employee");
                }
            }
        }

        logger.debug("Ending eduPersonAffiliations were: " + eduPersonAffiliation.getValues());
        ]]></Script>
    </resolver:AttributeDefinition>

    <resolver:AttributeDefinition id="eduPersonPrimaryAffiliation" xsi:type="ad:Script" xmlns="urn:mace:shibboleth:2.0:resolver:ad">
        <resolver:Dependency ref="eduPersonAffiliation" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1String" name="urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation" encodeType="false" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.5" friendlyName="eduPersonPrimaryAffiliation" encodeType="false" />
        <Script><![CDATA[
        var LoggerFactory = Java.type("org.slf4j.LoggerFactory");
        logger = LoggerFactory.getLogger("edu.internet2.middleware.shibboleth.resolver.script.affiliation");

        // set a Primary Affiliation, start with member
        eduPersonPrimaryAffiliation.addValue("member");
        var student = "false";
        var staff = "false";

        for ( i = 0; eduPersonAffiliation != null && i < eduPersonAffiliation.getValues().size(); i++ ){
            value = eduPersonAffiliation.getValues().get(i).toString();
            if (value.indexOf("staff") >= 0){
                staff = "true";
            }
            if (value.indexOf("student") >= 0){
                student = "true";
            }
        }

        if (staff === "true") { 
           eduPersonPrimaryAffiliation.getValues().clear();
           eduPersonPrimaryAffiliation.addValue("staff");
        } else if (student === "true") {   
           eduPersonPrimaryAffiliation.getValues().clear();
           eduPersonPrimaryAffiliation.addValue("student");   
        }

        logger.debug("Ending eduPersonPrimaryAffiliation was " + eduPersonPrimaryAffiliation.getValues());
        ]]></Script>
    </resolver:AttributeDefinition>

    <resolver:AttributeDefinition xsi:type="ad:Scoped" id="eduPersonScopedAffiliation" scope="%{idp.scope}" sourceAttributeID="eduPersonAffiliation">
        <resolver:Dependency ref="campusActiveDirectory" />
        <resolver:AttributeEncoder xsi:type="enc:SAML1ScopedString" name="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" encodeType="false" />
        <resolver:AttributeEncoder xsi:type="enc:SAML2ScopedString" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" friendlyName="eduPersonScopedAffiliation" encodeType="false" />
    </resolver:AttributeDefinition>

 

cccId

The cccid is a relatively new identifier within the CCC, that came into existence with CCC Apply. But it has become a critical attribute and we've been strongly encouraging the colleges and districts to add cccId as an attribute in their directory for students. There will be a growing number of use cases where having easy access to that attribute will be important, and its generally easier for a wider range of apps and services to access attributes from Active Directory or other LDAP directory than from the Student Information System of choice.

You can consider defining a "purpose-built" attribute to your AD/LDAP schema to hold cccId, but a common practice is to use some "otherwise unused standard attribute" to hold the value, with employeeNumber being one frequent choice. What works for you/your AD/LDAP directory will vary. But the following shows an example where employeeNumber has been used.

	<resolver:AttributeDefinition xsi:type="ad:Simple" id="cccId" sourceAttributeID="employeeNumber">	
        <resolver:Dependency ref="campusActiveDirectory" />
		<resolver:AttributeEncoder xsi:type="enc:SAML2String" name="https://www.openccc.net/saml/attributes/cccId" friendlyName="cccId" encodeType="false" />
		<resolver:AttributeEncoder xsi:type="enc:SAML2String" name="https://www.openccc.net/saml/attributes/cccId" friendlyName="cccId" encodeType="false" />
	</resolver:AttributeDefinition>
 

 

cccMisCode

The cccMisCode is a 3-digit number that has been assigned to every college and district within the CCC. This value would be the same for everyone in your directory, except for the complication where the district runs the IdP, and that IdP handles users from multiple colleges. In this latter case, it's not clear that there is any value in the IdP sending a value for the CCC MIS code, but our standard practice to date is to go ahead and set the MIS code in such a case to the district-level assigned MIS code. Here is a standard way one can define this in the IdP, where in thsi case the example MIS code is '123'.

(All of the CCC MIS codes are defined in the PDF available at this link: http://extranet.cccco.edu/Portals/1/TRIS/MIS/Left_Nav/DED/Appendices/App_A_District&Coll_Codes.pdf .)

	<resolver:AttributeDefinition xsi:type="ad:Simple" id="cccMisCode" sourceAttributeID="cccMisCode">
    	<resolver:Dependency ref="staticAttributes" />
    	<resolver:AttributeEncoder xsi:type="enc:SAML2String" name="https://www.openccc.net/saml/attributes/cccMisCode" friendlyName="cccMisCode" encodeType="false" />
    	<resolver:AttributeEncoder xsi:type="enc:SAML2String" name="https://www.openccc.net/saml/attributes/cccMisCode" friendlyName="cccMisCode" encodeType="false" />
	</resolver:AttributeDefinition>
 
....
 
    <!-- DataConnector to set MIS code attribute-->
    <resolver:DataConnector id="staticAttributes" xsi:type="dc:Static">
        <dc:Attribute id="cccMisCode">
            <dc:Value>123</dc:Value>
        </dc:Attribute>
    </resolver:DataConnector>
 

 

Full Shibboleth IdP v3 attribute resolver configuration file (attribute-resolver.xml) examples

  File Modified

XML File one-directory-example-attribute-resolver.xml

Aug 25, 2016 by Chris Franz

XML File two-directory-example-attribute-resolver.xml

Aug 25, 2016 by Chris Franz