SAML IdP with complex type attributes

126 views
Skip to first unread message

Petr Fišer

unread,
Feb 10, 2022, 11:14:21 AM2/10/22
to cas-...@apereo.org
Hello,
I am operating CAS 6.2.x as SAML2 IdP. One of our SP needs to get more complex structure in the attribute statements, basically something like this:

<saml2:Attribute FriendlyName="groups" Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
    <saml2:AttributeValue>
        <Group code="bla1" displayName="bla1"/>
    </saml2:AttributeValue>
    <saml2:AttributeValue>
        <Group code="bla2" displayName="bla2"/>
    </saml2:AttributeValue>
    ...
</saml2:Attribute>

This should be possible by defining attribute "groups" with type XSObject and CAS should then marshall it as a XML document inside the values. https://apereo.github.io/cas/6.2.x/installation/Configuring-SAML2-Attribute-Release.html
I am struggling in getting the setup to work... could somebody point me to the right direction, please?

Cheers,

The result i am getting:
<saml2:Attribute FriendlyName="groups" Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
    <saml2:AttributeValue>&lt;String&gt;Group@2abf08d0&lt;/String&gt;</saml2:AttributeValue>
    <saml2:AttributeValue>&lt;String&gt;Group@86c9d74&lt;/String&gt;</saml2:AttributeValue>
    <saml2:AttributeValue>&lt;String&gt;Group@5bdf462c&lt;/String&gt;</saml2:AttributeValue>
    <saml2:AttributeValue>&lt;String&gt;Group@6715cd46&lt;/String&gt;</saml2:AttributeValue>
</saml2:Attribute>

Part of CAS log:
2022-02-10 14:09:36,169 TRACE [org.apereo.cas.support.saml.util.AbstractSamlObjectBuilder] - <Attempting to generate SAML attribute [groups] with value(s) [[Role@2abf08d0, Group@86c9d74, Group@5bdf462c, Group@6715cd46]]>
2022-02-10 14:09:36,169 DEBUG [org.apereo.cas.support.saml.util.AbstractSamlObjectBuilder] - <Generating multi-valued SAML attribute [groups] with values [[Group@2abf08d0, Group@86c9d74, Group@5bdf462c, Group@6715cd46]]>
2022-02-10 14:09:36,169 TRACE [org.apereo.cas.support.saml.util.AbstractSamlObjectBuilder] - <Creating new attribute value XMLObject for value: [Group@2abf08d0], value type: [XSObject], QName: [{urn:oasis:names:tc:SAML:2.0:assertion}AttributeValue]>
2022-02-10 14:09:36,225 TRACE [org.apereo.cas.support.saml.util.AbstractSamlObjectBuilder] - <Created attribute value XMLObject: [org.opensaml.core.xml.schema.impl.XSAnyImpl@a859946]>
2022-02-10 14:09:36,225 TRACE [org.opensaml.core.xml.AbstractXMLObject] - <Releasing cached DOM reprsentation for parent of {urn:oasis:names:tc:SAML:2.0:assertion}AttributeValue with propagation set to true>
2022-02-10 14:09:36,225 TRACE [org.opensaml.core.xml.AbstractXMLObject] - <Releasing cached DOM reprsentation for {urn:oasis:names:tc:SAML:2.0:assertion}Attribute>
2022-02-10 14:09:36,225 TRACE [org.opensaml.core.xml.AbstractXMLObject] - <Releasing cached DOM reprsentation for parent of {urn:oasis:names:tc:SAML:2.0:assertion}Attribute with propagation set to true>

My setup:
1. LDAP server configured as attribute repository, each user has a "memberOf" attribute. This works, CAS can see the "memberOf".

2. I have a set of attributes produced by CAS, defined at service level. They are produced by inline groovy (works fine) with Mapped release policy.

3. Attribute "groups" produced by an external groovy script and GroovySaml policy. This script takes "memberOf" and creates an array of custom objects called Group. Script executes fine, but objects and not properly marshalled into SAML response.

Relevant part of service definition:
"attributeReleasePolicy":
{
"@class": "org.apereo.cas.services.ChainingAttributeReleasePolicy",
"mergingPolicy": "add",
"policies": [ "java.util.ArrayList",
  [
    {
  "@class": "org.apereo.cas.services.ReturnMappedAttributeReleasePolicy",
  "allowedAttributes": {
    "@class": "java.util.TreeMap",
    "attr1": "groovy { return 'val1' }",
    "attr2": "groovy { return 'val2' }"
  },
  "principalAttributesRepository":
  {
    "@class": "org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository",
    "mergingStrategy": "MULTIVALUED",
    "ignoreResolvedAttributes": false
  }
},
{
  "@class": "org.apereo.cas.support.saml.services.GroovySamlRegisteredServiceAttributeReleasePolicy",
  "groovyScript": "file:/etc/cas/config/groups-SAML.groovy",
  "order": 1
    }
  ]
],
"excludeDefaultAttributes": "true",
"authorizedToReleaseAuthenticationAttributes": "false",
"order": 0
},
"attributeValueTypes":
{
"@class": "java.util.LinkedHashMap",
"groups": "XSObject"
}

Relevant part of groups-SAML.groovy script:
def Map<String, Object> run(final Object... args) {
    def attributes = args[0];
    def service = args[1];
    def resolver = args[2];
    def facade = args[3];
    def entityDescriptor = args[4];
    def applicationContext = args[5];
    def logger = args[6];

    LinkedHashMap<String,Object> res = new LinkedHashMap<String,Object>();
    ArrayList<Object> vals = new ArrayList<Object>();
    for (o : attributes["memberOf"]) {
        String s = parseName((String) o);
        Group r = new Group(s);
        vals.add(r);
    }
    res.put("groups",vals);

    return res;
}

class Group extends XSAnyImpl {

    private String textContent = null;
    private LinkedHashMap<QName,String> unknownAttributes;
    private IndexedXMLObjectChildrenList<XMLObject> unknownXMLObjects = null;

    public Group(String s) {
        // https://javadoc.io/static/org.opensaml/opensaml-core/3.4.3/org/opensaml/core/xml/schema/impl/XSAnyImpl.html
        // https://docs.oracle.com/javase/7/docs/api/javax/xml/namespace/QName.html?is-external=true
        super("blablabla","Group",null);
        this.unknownAttributes = new LinkedHashMap<QName,String>();
        this.unknownAttributes.put(new QName("code"), s);
        this.unknownAttributes.put(new QName("displayName"), s);
    }
}

Petr Fišer

unread,
Mar 4, 2022, 6:36:52 AM3/4/22
to CAS Community, Petr Fišer
Hello,
Just a heads up on this. After some debugging, I suspect there is an issue somewhere after the attributes get loaded into attribute repository. That's where List<Group> gets garbled into List<String> and because Group.toString() is not defined, the list gets filled with Java identifiers of Group instances. The SAML document release part of the flow receives already garbled data.

I upgraded to CAS 6.5 and this problem is solved there. Data structure correctly gets to the AbstractSamlObjectBuilder and you can even set the namespace, attributes, etc. by using Jackson annotations.
So the POJO in the attribute transform might look like this (example):

@JacksonXmlRootElement(namespace="somenamespace", localName="Group")
class Group {
    @JacksonXmlProperty(isAttribute = true)
    String attribute = "value";
    String element = "elementvalue";
    ...
}

The problem I am facing now seems to be in the AbstractSamlObjectBuilder which correctly builds the object, but pushes it to text content of the parent attribute. This means that output looks like:
&lt;Group xmlns=&quot;somenamespace&quot; attribute=&quot;value&quot;&gt;&lt;element&gt;elementvalue&lt;/element&gt;&lt;/Group&gt;
instead of:
<Group xmlns="somenamespace" attribute="value"><element>elementvalue</element></Group>

Cheers,
Fiisch
Dne čtvrtek 10. února 2022 v 17:14:21 UTC+1 uživatel Petr Fišer napsal:
Reply all
Reply to author
Forward
0 new messages