Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

However, in a few cases the AttributeDefinition will not have a Encoder. This attribute cannot be released and cannot generate SAML on its own, but it can be used to create a second attribute. This is most useful when several attributes have to be combined to produce a single new attribute. Attributes can be used to generate variables in a JavaScript generated attributes, or they can define variables inserted into a SQL statement in a secondary query, or they can be combined to form a composite text string in a Template attributes. JavaScript seems to be the easiest case to explain.

appear just as in the box above. Without an Encoder it cannot be used to generate SAML, but it serves a different purpose to fix a bug in the Shibboleth configuration schema.

Previously defined attributes can be used to define secondary attributes. This can occur when you define an Attribute with a block of JavaScript code, or if you need to plug the attribute into a subsequent query or insert it into a string.

So here is the problem. An AttributeDefinition can have any number of Dependency statements, but only one can meaningfully reference a DataConnector (that is a Query). For that one Dependency, the sourceAttributeID on the AttributeDefinition selects one column or property and makes that variable available as a pre-defined JavaScript variable with the sourceAttributeID= name. Every other Dependency has to reference an AttributeDefinition instead of a DataConnector. Each of those other Dependencies creates a pre-defined JavaScript variable with the ref= name on the Dependency statement (which is the id= of the referenced AttributeDefinition. If an AttributeDefinition had two different DataConnectors referenced by two Dependency elements, then Shibboleth would not know which Query contained the sourceAttributeID column, and if both queries had the sourceAttributeID as a column name it could not create two JavaScript variables with the same name. So using a separate AttributeDefinition like the one in the box above creates an unambiguous statement. A Dependency with a ref="idrFirstName" creates a JavaScript variable named "idrFirstName" that has the value of the "FirstName" column returned by the result set of the "IDRQuery" DataConnector. Any other first names from any other queries can be similarly give unique identifiers.

 

Note: Oracle queries return column names that are all UPPERCASE. It is a best practice to use uppercase names in the query and in all subsequent references to the column. If you specify an Oracle column name in lower or mixed case in subsequent XML, then the Java code will fail to match the UPPERCASE name in the Oracle result set and a null or missing value will be returnedAn AttributeDefinition can have any number of Dependency statements, but only one can reference a DataConnector (that is a Query) because there is only the one sourceAttributeID. That sourceAttributeID selects a column or property in the single query. In a Script definition, the sourceAttributeID also names the JavaScript pre-defined variable passed to the JavaScript that holds the value of this column or property. However, the script may require the value of other columns from the same or other queries, and the only way to get them is to create named (id=name) attributes with another AttributeDefinition. All the other Dependency statements (that don't reference a DataConnector, reference the id of another AttributeDefinition. The JavaScript block gets a variable with that id name and the value of that attribute.

An AttributeDefinition referenced by the Dependency statement in a JavaScript block can also have AttributeEncoder statements and be released as SAML in a Response. It can do both jobs simultaneously. An AttributeDefinition that doesn't have an Encoder can only be referenced by a Dependency statement in the definition of another attribute.

XML and Shibboleth are case sensitive, so it is important to realize that Oracle always converts its columns to UPPERCASE. To avoid errors you should always use UPPERCASE names for the sourceAttributeID field if the query is to an Oracle database, and you should define an UPPERCASE id for a default static value in the fallback connector if the Oracle query fails. Otherwise you may spend hours trying debugging the failure of the value to show up where you expect it to be.

When designing this file, you need to consider certain possibilities:

  • The database query can return no rows.
  • The database query can return a SQL NULL value for a column (unless you use NVL in Oracle or ISNULL in SQL Server to replace the NULL with a default value).
  • An LDAP query can return no User object
  • An LDAP query can return a User object, but in that object the property you are looking for may not be present.
  • An LDAP query can return a User object, and the property may be present, but it may have no values in the list of values.

First, any value that might be used as a SAML Subject for any Yale person in any login cannot be missing or null for any person at Yale. That makes sense for SAML, but Yale has a few partners who request a particular value be presented as Subject. If the partner only provides data for Employees and it makes no sense for a Student to ever try to login to that partner, you might imagine that this is OK. However, Shibboleth will generate a null pointer exception if any query return NULL for any attribute marked as a possible Subject for any login even if it isn't going to be the Subject in this particular login. So use NVL or ISNULL to replace possibly NULL values with 0 or -1 or "undefined" in such cases.

Then in JavaScript, variables generated from queries with no rows, or no objects, or no property, or NULL can end up being a "undefined" JavaScript variable (a variable that has never been declared with any value) or a defined variable with a value of JavaScript null, or a variable whose value is an object that has no values in its collection of values. Generally speaking, you should always write your JavaScript code to check for all possible bad results, because if you try to test if a variable has the value null when it is really "undefined" you are going to throw an exception. See the Yale code for examples of appropriately paranoid variable testing.

Returning no rows or objects is a normal response to a query. A query fails if it generates an ORAxxxx SQLException or a NamingException. Typically this happens if the database server or directory is down, but it can also happen if the userid and password you are using to login to the server is no longer valid or if permissions have been revoked or were never granted to that user.

...

  • , but it may have no values in the list of values.

First, a Subject cannot be NULL. Therefore, you cannot define a Subject (a NameID) on any column or property that can be NULL or missing for anyone (student, faculty, staff, alumnus, contractor, ...).

Then you have to write JavaScript code very carefully.

A JavaScript variable is "undefined" if the variable name has never been used and is not in the name table. This can occur indirectly if an LDAP query returns a user object that does not have the property whose name you specified in the sourceAttributeID (although this may vary from version to version of Shibboleth and Java). There is a special way to check JavaScript variables to see if they are undefined and it is not "if (name==null)" because that throws an exception if "name" is undefined.

A JavaScript variable can be null. However, when the JavaScript variable is associated with an attribute then it points to an Attribute object. The object contains an array of values (because any Attribute could be multi-valued). So it is also possible that the array of values is empty. Each of these possibilities is a different test with different syntax, and you have to do all of them before you can just use a JavaScript attribute and assume it has a value.

Because it is easy to screw up, run any block of JavaScript in a try-catch, and if you catch an error then it is a reasonable practice to assume that some variable you are using is missing or has no value and generate the appropriate result based on that assumption.

If any exception is thrown during the query, then the Shibboleth code will attempt to execute a secondary query specified in the "failover" attribute of the DataConnector. The failover can point to a different query to a different database that might return the same value. Or it can be a Static element.

...

Every Query must have a Failover DataConnector, which may itself have a Failover, and the chain must end with a Static Connector.

 

Yale reorganized its attribute-resolver.xml file to emphasize the importance of a Static Failover for every query.

In vanilla Shibboleth, the only errors that are caught during Attribute evaluation are query errors. Any other error (during JavaScript evaluation, or because of Shibboleth bugs) is fatal and prevents the user from logging on to any partner. Yale has added code to wrap other evaluations with a try-catch that discards attributes in error but preserves all the other attributes. Because attribute errors tend to occur only in new attributes not widely in use or old abandoned attributes no longer in use, this makes Shibboleth more robust against real world errors without impacting security. We will try to interest the Shibboleth maintainers in making this fix standard.

Once you have the data from queries, the second step is to format them as Attributes. An attribute contains the value, but it is accompanied by some names and types.

Different partners have decided to demand that the same piece of information be given different names when sent to them. Take something as simple as "first name". In the West, the last name is the family name, but in China the first name is the family name. So international standards prefer not to base the attribute name on its position. Of course, many of our partners only service the US. So for "Howard Gilbert", the "Howard" value will be assigned to many Attributes with names such as without impacting security. We will try to interest the Shibboleth maintainers in making this fix standard.

The same value may be used to generate many different Attributes with various friendlyName and name values. There are either no real standards, or too many real standards (which is pretty much the same thing). The first name column could be used to generate attributes for "FirstName", "firstName", "first_name", "givenName", "Given Name", and slightly more sophisticated partners will ask for one of three globally unique technical identifiers ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", "http://schemas.xmlsoap.org/claims/FirstName", and "urn:oid:2.5.4.42"). Multiple Attributes with different names can all refer back to the same query and the same database column or LDAP property name. Each Attribute is assigned a unique ID value, and the appropriately named Attribute is then released to each partner in the attribute-filter fileIn part this is caused by the convention in the Far East for the first name to be the family name, while in the West that is the last name.

It is also possible to define several Attributes with the same name but different values. For example, the attribute with name "mail" (the preferred E-Mail address) can be an alias or a direct mailbox address (that is, it can be howard.gilbert@yale.edu or howard.gilbert.eliapps@bulldogs.yale.edu). Which mail address to send depends on whether you are logging into Box or Coursera. So alternative Attributes with name "mail" are defined and given values, and then one of them can be released to each partner using the attribute-filter file.

...