This discussion is archived
1 Reply Latest reply: Oct 21, 2013 5:04 AM by hyperipwn RSS

SAML2 Populate Subject NameID with Attribute Value

hyperipwn Newbie
Currently Being Moderated

Hello,

 

I have a very specific requirement and I am not confident with Java development so I really hope someone can help me with this problem.

 

Basically we have an issue with SAML2 SSO. We do not manage the Federation Services servers and those servers return a specific attribute as the subject, like so:

 

<saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">USERNAME</saml:NameID>

 

We can ask for more attributes to be returned in the SAML2 response and they will be added, like so:

 

<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml:AttributeValue xsi:type="xs:string">my.name@example.com</saml:AttributeValue>

 

However we need to use the value of the "mail" attribute rather than the Subject NameID as the principal name to be passed back to the WebLogic application. The application is EPM Foundation Services and has been set up to use an LDAP authentication source that uses "mail" as the username. We cannot use the NameID. As such we see errors like the below in the WL logs for FoundationServices0, when we attempt SAML2 SSO:

 

<DefaultSAML2NameMapperImpl: mapName: Mapped name: qualifier: null, name: USERNAME>
<SAMLIACallbackHandler: SAMLIACallbackHandler(true, USERNAME, null)>
<SAMLIACallbackHandler: callback[0]: NameCallback: setName(USERNAME)> 
<[Security:090304]Authentication Failed: User USERNAME javax.security.auth.login.FailedLoginException: [Security:090302]Authentication Failed: User USERNAME denied>  

 

I believe we need to use a custom name mapper class to achieve the desired behaviour. However I cannot see in the APIs how we can achieve this behaviour. I have the skeleton of the class below, but like I said, I can't see how I can get from a SAML2NameMapperInfo object to a way to list all the attributes, as I could with a SAML2AttributeInfo object.

 

import java.util.HashSet;
import java.util.Set;
import javax.security.auth.Subject;
import com.bea.security.saml2.providers.SAML2CredentialNameMapper;
import com.bea.security.saml2.providers.SAML2IdentityAsserterNameMapper;
import com.bea.security.saml2.providers.SAML2NameMapperInfo;
import weblogic.security.SubjectUtils;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.WLSGroup;
public class MyNameMapper implements SAML2IdentityAsserterNameMapper {
   @Override
    public String mapNameInfo(SAML2NameMapperInfo saml2NameMapperInfo, ContextHandler arg1) {
        String user = saml2NameMapperInfo.getName(); 
       
        System.out.println(user);
        return user;
    }
}

 

I have used these pages as a reference:

Totapally: Open AM, and WebLogic Application Server - Single Sign On

https://twiki.cern.ch/twiki/bin/view/DB/CernWlsPrincipalMapper

Use SAML assertions from an application server acting as an identity provider

And the API docs.

 

Please help me understand how the above requirement could be achieved. I am aware that this is not a normal way to implement the Name Mapping but it is a requirement we have and all other avenues for fulfilling the requirement have already been explored and have failed.

  • 1. Re: SAML2 Populate Subject NameID with Attribute Value
    hyperipwn Newbie
    Currently Being Moderated

    Hi,

     

    I managed to put something together from a few different examples online. In case anyone else gets completely stuck with this issue here is the code I have as of now:

     

    MyNameMapper.class:

    package sample.providers.saml;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.Arrays;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import javax.security.auth.Subject;
    import java.security.Principal;
    import com.bea.security.saml2.providers.SAML2AttributeInfo;
    import com.bea.security.saml2.providers.SAML2AttributeStatementInfo;
    import com.bea.security.saml2.providers.SAML2CredentialNameMapper;
    import com.bea.security.saml2.providers.SAML2IdentityAsserterNameMapper;
    import com.bea.security.saml2.providers.SAML2IdentityAsserterAttributeMapper;
    import com.bea.security.saml2.providers.SAML2NameMapperInfo;
    import weblogic.security.service.ContextHandler;
    import weblogic.security.SubjectUtils;
    import weblogic.security.service.ContextHandler;
    import weblogic.security.spi.WLSUser;
    import weblogic.security.spi.WLSGroup;
    import weblogic.security.principal.*;
    public class MyNameMapper implements SAML2IdentityAsserterNameMapper, SAML2IdentityAsserterAttributeMapper {
       @Override
        public String mapNameInfo(SAML2NameMapperInfo saml2NameMapperInfo, ContextHandler arg1) {
            String user;
            System.out.println("--- MyNameMapper ---");
            // System.out.println("MyNameMapper mapNameInfo");
            // System.out.println("saml2NameMapperInfo: " + saml2NameMapperInfo.toString());
            // System.out.println("arg1: " + arg1.toString());
            // System.out.println("arg1 number of elements: " + arg1.size());
            
            // getNames gets a list of ContextElement names that can be requested.
            String[] arr = arg1.getNames();
            
            // For each possible element
            for (String element : arr) {
                System.out.println("ContextHandler elements: " + element);
                // If one of those possible elements has the AttributePrinciples
                if(element.equals("com.bea.contextelement.saml.AttributePrincipals")){
                    // Put the AttributesPrincipals into an ArrayList of CustomPrincipals
                    ArrayList <CustomPrincipal> arr2 = (ArrayList <CustomPrincipal>) arg1.getValue("com.bea.contextelement.saml.AttributePrincipals");
                    int i = 0;
                    String attr;
                    if(arr2 != null){
                        // For each AttributePrincipal in the ArrayList
                        for (CustomPrincipal element2 : arr2) {
                            // Get the Attribute Name and the Attribute Value
                            attr = element2.toString();
                            System.out.println("Attribute " + i + " Name: " + attr);
                            System.out.println("Attribute " + i + " Value: " + element2.getCollectionAsString());
                            // If the Attribute is "loginAccount"
                            if(attr.equals("loginAccount")){
                                user = element2.getCollectionAsString();
                                // Remove the "@DNS.DOMAIN.COM" (case insensitive) and set the username to that string
                                if(!user.equals("null")){
                                    System.out.println("Username (from loginAccount): " + user.replaceAll("(?i)\\@CLIENT\\.COMPANY\\.COM", ""));
                                    return user.replaceAll("(?i)\\@CLIENT\\.COMPANY\\.COM", "");
                                }
                            }
                            i++;
                        }
                    }
                    
                    // For some reason the ArrayList of CustomPrincipals was blank - just set the username to the Subject 
                    
                    user = saml2NameMapperInfo.getName(); // Subject = BRID
                    System.out.println("Username (from Subject): " + user);
                    System.out.println("--- MyNameMapper ---");
                    return user;
                }
            }
            
            // Just in case AttributePrincipals does not exist
            user = saml2NameMapperInfo.getName(); // Subject = BRID
            System.out.println("Username (from Subject): " + user);
            System.out.println("--- MyNameMapper ---");
            // Set the username to the Subject 
            return user;
            
            // System.out.println("com.bea.contextelement.saml.AttributePrincipals: " + arg1.getValue("com.bea.contextelement.saml.AttributePrincipals"));
            // System.out.println("com.bea.contextelement.saml.AttributePrincipals CLASS: " + arg1.getValue("com.bea.contextelement.saml.AttributePrincipals").getClass().getName());
            
            
            // System.out.println("ArrayList toString: " + arr2.toString());
            // System.out.println("Initial size of arr2: " + arr2.size());
            
            
        }
        public Collection<Object> mapAttributeInfo0(Collection<SAML2AttributeStatementInfo> attrStmtInfos, ContextHandler contextHandler) {
            if (attrStmtInfos == null || attrStmtInfos.size() == 0) {
                System.out.println("CustomIAAttributeMapperImpl: attrStmtInfos has no elements");
                return null;
            }
             
            Collection<Object> customAttrs = new ArrayList<Object>();
             
            for (SAML2AttributeStatementInfo stmtInfo : attrStmtInfos) {
                Collection<SAML2AttributeInfo> attrs = stmtInfo.getAttributeInfo();
                if (attrs == null || attrs.size() == 0) {
                    System.out.println("CustomIAAttributeMapperImpl: no attribute in statement: " + stmtInfo.toString());
                } else {
                for (SAML2AttributeInfo attr : attrs) {
                    if (attr.getAttributeName().equals("AttributeWithSingleValue")){
                            CustomPrincipal customAttr1 = new CustomPrincipal(attr.getAttributeName(), attr.getAttributeNameFormat(),attr.getAttributeValues());
                            customAttrs.add(customAttr1);
                    }else{
                        String customAttr = new StringBuffer().append(attr.getAttributeName()).append(",").append(attr.getAttributeValues()).toString();
                        customAttrs.add(customAttr);
                    }
                }
                }
            }
            return customAttrs;
        }
         
        public Collection<Principal> mapAttributeInfo(Collection<SAML2AttributeStatementInfo> attrStmtInfos, ContextHandler contextHandler) {
            if (attrStmtInfos == null || attrStmtInfos.size() == 0) {
                // System.out.println("CustomIAAttributeMapperImpl: attrStmtInfos has no elements");
                return null;
            }
         
            Collection<Principal> pals = new ArrayList<Principal>();
             
            for (SAML2AttributeStatementInfo stmtInfo : attrStmtInfos) {
                Collection<SAML2AttributeInfo> attrs = stmtInfo.getAttributeInfo();
                if (attrs == null || attrs.size() == 0) {
                    // System.out.println("CustomIAAttributeMapperImpl: no attribute in statement: " + stmtInfo.toString());
                } else {
                    for (SAML2AttributeInfo attr : attrs) {
                        CustomPrincipal pal = new CustomPrincipal(attr.getAttributeName(), attr.getAttributeNameFormat(), attr.getAttributeValues());
                        pals.add(pal);
                    }
                }
            }
            return pals;
        }
    }

     

    CustomPrincipal.class:

    package sample.providers.saml;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.Arrays;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    import javax.security.auth.Subject;
    import java.security.Principal;
    import com.bea.security.saml2.providers.SAML2AttributeInfo;
    import com.bea.security.saml2.providers.SAML2AttributeStatementInfo;
    import com.bea.security.saml2.providers.SAML2CredentialNameMapper;
    import com.bea.security.saml2.providers.SAML2IdentityAsserterNameMapper;
    import com.bea.security.saml2.providers.SAML2IdentityAsserterAttributeMapper;
    import com.bea.security.saml2.providers.SAML2NameMapperInfo;
    import weblogic.security.service.ContextHandler;
    import weblogic.security.SubjectUtils;
    import weblogic.security.service.ContextHandler;
    import weblogic.security.spi.WLSGroup;
    import weblogic.security.spi.WLSUser;
    import weblogic.security.principal.*;
    public class CustomPrincipal extends WLSAbstractPrincipal implements WLSUser{
        private String commonName;
        private Collection collection;
        public CustomPrincipal(String name, String string, Collection<String> collection) {
            super();
            // Feed the WLSAbstractPrincipal.name. Mandatory
            System.out.println("--- CustomPrincipal ---");
            this.setName(name);
            this.setCommonName(name);
            this.setCollection(collection);
            System.out.println("--- CustomPrincipal ---");
        }
        
        public CustomPrincipal() {
            super();
        }
        
        public CustomPrincipal(String commonName) {
            super();
            this.setName(commonName);
            this.setCommonName(commonName);
        }
         
        public void setCommonName(String commonName) {
            // Feed the WLSAbstractPrincipal.name. Mandatory
            super.setName(commonName);
            this.commonName = commonName;
            System.out.println("Attribute: " + this.getName());
            // System.out.println("Custom Principle commonName is " + this.commonName);
        }
              
        public Collection getCollection() {
            return collection;
        }
        
        public String getCollectionAsString() {
            String collasstr;
            if(collection != null && collection.size()>0){
                for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
                    collasstr = (String) iterator.next();
                    return collasstr;
                }
            }
            return "null";
        }
        
        public void setCollection(Collection collection) {
            this.collection = collection;
            // System.out.println("set collection in CustomPrinciple!");
            if(collection != null && collection.size()>0){
                for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
                    final String value = (String) iterator.next();
                    System.out.println("Attribute Value: " + value);
                }
            }
        }
        
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = super.hashCode();
            result = prime * result + ((collection == null) ? 0 : collection.hashCode());
            result = prime * result + ((commonName == null) ? 0 : commonName.hashCode());
            return result;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (!super.equals(obj))
                return false;
            if (getClass() != obj.getClass())
                return false;
            CustomPrincipal other = (CustomPrincipal) obj;
            if (collection == null) {
                if (other.collection != null)
                    return false;
            } else if (!collection.equals(other.collection))
                    return false;
            if (commonName == null) {
                if (other.commonName != null)
                    return false;
            } else if (!commonName.equals(other.commonName))
                    return false;
            return true;
        }
         
    }

     

    Please note that very little of the above code is mine and everything that I used is public domain. The full code above is also public domain.

     

    Thanks,

     

    -Dan