10

I'm using Spring Security SAML 1.0.1, and I want to know the value of the SAML attribute whose name is "eduPersonAffiliation". I've coded a class which implements the org.springframework.security.saml.userdetails.SAMLUserDetailsService interface and in the loadUserBySAML method, I'm doing this:

@Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
    String eduPersonAffiliationAttributeName = "";
    // We need to use the "name" of the attribute to retrieve the value (not the friendly name)
    for (Attribute attribute : credential.getAttributes()) {
        if ("eduPersonAffiliation".equals(attribute.getFriendlyName())) {
            eduPersonAffiliationAttributeName = attribute.getName();
        }
    }
    Person user = usersService.getUser(
             credential.getAttribute(eduPersonAffiliationAttributeName).WHAT_TO_CALL_HERE?);
    return loadUserByUser(user);
}

The getUser method expects a String which should be the login of the connected user. The question sounds stupid but how can I access the attribute value given the attribute name? I see a org.opensaml.saml2.core.getAttributeValues method that returns a List<XMLObject>. How to use it?

Thanks!

Grégoire C
  • 1,291
  • 1
  • 13
  • 31

2 Answers2

24

XmlObject requires some unpacking to work with:

private String getAttributeValue(XMLObject attributeValue)
{
    return attributeValue == null ?
            null :
            attributeValue instanceof XSString ?
                    getStringAttributeValue((XSString) attributeValue) :
                    attributeValue instanceof XSAnyImpl ?
                            getAnyAttributeValue((XSAnyImpl) attributeValue) :
                            attributeValue.toString();
}

private String getStringAttributeValue(XSString attributeValue)
{
    return attributeValue.getValue();
}

private String getAnyAttributeValue(XSAnyImpl attributeValue)
{
    return attributeValue.getTextContent();
}

You can loop over the List<XmlObject> until you find the attribute you need to and then call the getAttributeValue(XmlObject) method above.

Depending on what these XmlObjects really are (Attribute, AttributeValue, etc.) you may need some portion of this algorithm to unpack them fully:

private final static String USERNAME_ATTRIBUTE_NAME = "urn:oid:0.9.2342.19200300.100.1.3"

private String getUsername(Assertion assertion)
{
    for (AttributeStatement attributeStatement : assertion.getAttributeStatements())
    {
        for (Attribute attribute : attributeStatement.getAttributes())
        {
            if (USERNAME_ATTRIBUTE_NAME.equals(attribute.getName()))
            {
                List<XMLObject> attributeValues = attribute.getAttributeValues();
                if (!attributeValues.isEmpty())
                {
                    return getAttributeValue(attributeValues.get(0));
                }
            }
        }
    }
    throw new IllegalArgumentException("no username attribute found");
}

In this case I'm using the standard OID for email address. In practice this has to be configurable as various IdP's use different naming strategies. This worked with Shibboleth IdP 3.

@StefanRasmusson's A Guide to OpenSAML is what got me past the hump between getting SAML concepts and being able to implement my own SP.

Scott Cantor was also incredibly helpful to me on the shibboleth-users mailing list from topics ranging for configuration gaps to high-level security architectural questions. The OpenSAML community (including Shibboleth) are very helpful and opinionated and I like that.

Alain O'Dea
  • 18,452
  • 1
  • 45
  • 69
  • @GrégoireColbert does this work for you or get you closer to the answer. I can iterate on it with you. – Alain O'Dea Nov 12 '15 at 18:31
  • Thank you Alain, I'll try this tomorrow ! – Grégoire C Nov 12 '15 at 18:55
  • Good stuff. @ mention me and I'll jump in. – Alain O'Dea Nov 12 '15 at 18:56
  • I get an infinite loop when using the`getAttributeValue` method. I suppose that the reading of the attribute depending on its class is done by a Spring Security SAML function, which is named differently? In other words, what would be the correct call for `getAttributeValue((XSString) attributeValue)`? – Grégoire C Nov 13 '15 at 11:23
  • I'm looking for the correct method in https://wiki.shibboleth.net/confluence/display/OpenSAML/OSTwoUserManual but for the moment, I haven't found it. – Grégoire C Nov 13 '15 at 11:29
  • @GrégoireColbert that's my fault. I'm missing the other typed versions of that method. Sorry. I'll bring those in now. – Alain O'Dea Nov 13 '15 at 11:42
  • @GrégoireColbert my bad for missing that. I tend to follow Uncle Bob's "extract 'til you drop" approach to get smaller methods. I need better names for those other two methods as they are in the same type hierarchy. I'll fix that now. – Alain O'Dea Nov 13 '15 at 11:45
  • 1
    @GrégoireColbert I've fixed it now. Thank you for pointing that out. – Alain O'Dea Nov 13 '15 at 11:49
  • 1
    This is actually a very well written piece of code @Alain, worked like charm :) – Milan Desai Dec 16 '20 at 23:19
  • Thank you @MilanDesai. That is very kind :) – Alain O'Dea Dec 16 '20 at 23:19
2

Another solution to access an attribute value is via SAMLCredential.getAttributeAsString(String name)

credential.getAttributeAsString(attribute.getName())
Neonchen
  • 71
  • 6