Supporting List Attributes through SAML 2.0 in PicketLink
tim.kutz Feb 28, 2013 2:11 PMWe have been developing several applications which are all authenticated using PicketLink and SAML2.0. In the simplest case, authentication works, and attributes are published. However, when a user has a list of values in an attribute, only one value of the list is published. After a great deal of effort stepping through and debugging the sequence, I believe I have tracked down each of the places that need to be changed in order to publish these attributes correctly. If the changes were confined to extension points alone, I would implement them myself, and move ahead. Unfortunately, they are not. Two of the places that need changed can be replaced as part of configuration, but the third involves the createAttributeStatement() method of the StatementUtil class.
Here is what I am seeing.
First, in org.jboss.security.mapping.providers.attribute.LdapAttributeMappingProvider, where the attribute values are retrieved from our LDAP server, the attributes are assumed to be single valued. The code in question, lines 238-256 of the version I'm currently looking at, is as follows:
while (results.hasMore()) { sr = (SearchResult) results.next(); Attributes attributes = sr.getAttributes(); NamingEnumeration<? extends javax.naming.directory.Attribute> ne = attributes.getAll(); while(ne != null && ne.hasMoreElements()) { javax.naming.directory.Attribute ldapAtt = ne.next(); if("mail".equalsIgnoreCase(ldapAtt.getID())) { attributeList.add(AttributeFactory.createEmailAddress((String) ldapAtt.get())); } else attributeList.add(AttributeFactory.createAttribute(ldapAtt.getID(), (String)ldapAtt.get())); } }
The line highlighted in red, as you can see, created an attribute with a single value (ldapAtt.get()). For a multivalued attribute, this should be iterating through multiple values. I'm unclear as to whether it should be creating multiple attributes, one for each value, all with the same name, or if it should be producing one attribute at this point, with multiple values. Either could work, but would need to be handled correctly in the AttributeManager implementation. As the Attributes being created here are parameterized types, I'm unsure if an Attribute<List<String>> is appropriate or not. More on this below, as it affects the implementation of AttributeManager.
In the AttributeManager, the list of Attributes generated above is converted into a map. If multiple values are treated as separate attributes in the list, then all but one will be lost, as they are added to the list, since they have the same key. Althought his would appear at first to point towards using Attribute<List<String>> to support these, there is an added twist. Multiple MappingProviders can be configured, and when this is the case, they each add their attributes to the same list. Thus, if there are attribute name collisions across the sources, values may be lost. It seems more appropriate that the AttributeManager merges the multiple entries as it creates the Map of values. I'm still not entirely sure of this, though, as it may have impact on other downstream code that I'm not familiar with.
Regardless of the two fixes above, multiple value still will not be published into the SAML. The final change is in the createAttributeStatement(Map<String,Object>) method of org.picketlink.identity.federation.core.saml.v2.util.StatementUtil. This method iterates through the map of attributes generated by the AttributeManager, and produces the Document objects for the Attribute section. It assumes that the attribute values are single valued. Interestingly, there is a special section of the code which handles Roles, but instead of adding multiple values of the attribute to the Attribute, it adds an additional Attribute for each role. This actually produces incorrect SAML output. Where it should produce:
<saml:AttributeStatement> <saml:Attribute Name="Role"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">CPDEV002</saml:AttributeValue> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">User</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement>
We instead get:
<saml:AttributeStatement> <saml:Attribute Name="Role"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">CPDEV002</saml:AttributeValue> </saml:Attribute> <saml:Attribute Name="Role"> <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">User</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement>
I've added the following Enhancement request in the issue tracker for this, as well.
https://issues.jboss.org/browse/PLFED-386
Although I'm happy to work on making these changes, I do need to make sure I take the correct approach, especially with regards to the Attribute<List<String>> vs multiple Attribute<String> question, and when/how to merge the multiple attributes of the same name.