This is a post to cover my forum post here: https://forums.oracle.com/message/11238560#11238560
Basically we had an issue with SAML2 SSO for EPM. 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>
So I had to write the two classes below:
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\\.EXAMPLE\\.COM", ""));
return user.replaceAll("(?i)\\@CLIENT\\.EXAMPLE\\.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;
}
}
This must be added as a Custom Name Mapper class in WebLogic:
sample.providers.saml.MyNameMapper
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.
No comments:
Post a Comment