package no.ntnu.qos.server.mediators.impl;
import java.util.Iterator;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import no.ntnu.qos.server.mediators.AbstractQosMediator;
import no.ntnu.qos.server.mediators.MediatorConstants;
import no.ntnu.qos.server.mediators.QosLogType;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseLog;
/**
* This mediator is meant to parse the message as it is coming in through
* the ESB and extract the SAML message to determine the role of the client.
* This mediator will also extract the service the client is trying to invoke
* and add both to the properties of the {@link MessageContext} so we can
* retrieve in the outsequence.
*
* @author Jørgen
* @author Ola Martin
*
*/
public class SAMLMediator extends AbstractQosMediator {
private static final QName friendlyName = new QName("FriendlyName");
private static final QName recipient = new QName(MediatorConstants.QOS_RECIPIENT);
private static final Pattern endpointPattern = Pattern.compile(
"(https|http)://[0-9]+.[0-9]+.[0-9]+.[0-9]+(:[0-9]+)?");
/**
* Set a variable to detach the SAML assertion
*/
private boolean detachAssertion = true;
@Override
public boolean mediateImpl(MessageContext synCtx, SynapseLog synLog) {
final String service = this.getService(synCtx);
final String clientRole = this.getClientRole(synCtx);
if(clientRole.isEmpty() || clientRole.trim().isEmpty()){
this.logMessage(synLog, "Could not " +
"find a valid client role in SAML assertion.\n" +
"Envelope was:\n" + synCtx.getEnvelope(), QosLogType.WARN);
return false;
}
if(service==null || service.isEmpty() || service.trim().isEmpty()){
this.logMessage(synLog, "Could not " +
"find a valid service endpoint in SAML assertion.\n" +
"Envelope was:\n" + synCtx.getEnvelope(), QosLogType.WARN);
return false;
}
synCtx.setProperty(MediatorConstants.QOS_SERVICE, service);
synCtx.setProperty(MediatorConstants.QOS_CLIENT_ROLE, clientRole);
this.logMessage(synLog, "Set client role to: " +
clientRole + ", set service to: " + service, QosLogType.INFO);
if(detachAssertion){
this.stripSAML(synCtx, synLog);
}
return true;
}
private void stripSAML(MessageContext synCtx, SynapseLog synLog) {
OMElement sa = getSAMLAssertion(synCtx.getEnvelope());
if(sa!=null){
sa.detach();
this.logMessage(synLog, "Stripped SAMLAssertion from message", QosLogType.INFO);
}
}
@SuppressWarnings("unchecked")
private OMElement getSAMLAssertion(SOAPEnvelope soapEnvl){
Iterator<OMElement> iter = soapEnvl.getBody().getChildElements();
return getOMElement(iter, "Assertion");
}
private OMElement getOMElement(final Iterator<OMElement> iter, final String localName){
OMElement result = null;
while(iter.hasNext()){
OMElement ch = (OMElement) iter.next();
if(ch.getLocalName().equalsIgnoreCase(localName)){
result = ch;
break;
}
}
return result;
}
@SuppressWarnings("unchecked")
private OMElement getSAMLAttributeStatement(OMElement samlAssertion){
if(samlAssertion != null){
Iterator<OMElement> iter = samlAssertion.getChildElements();
return getOMElement(iter, "AttributeStatement");
}
return null;
}
@SuppressWarnings("unchecked")
private OMElement getSAMLSubject(OMElement samlAssertion){
if(samlAssertion != null){
Iterator<OMElement> iter = samlAssertion.getChildElements();
return getOMElement(iter, "Subject");
}
return null;
}
@SuppressWarnings("unchecked")
private OMElement getSAMLSubjectConformation(OMElement samlSubject){
OMElement subject = this.getSAMLSubject(samlSubject);
if(subject != null){
return getOMElement(subject.getChildElements(), "SubjectConfirmation");
}
return null;
}
@SuppressWarnings("unchecked")
private String getClientRole(MessageContext ctx){
String result = "";
OMElement as = getSAMLAttributeStatement(getSAMLAssertion(ctx.getEnvelope()));
if(as != null){
//This is ugly, but there is no way to create an empty OMElement
//use null instead
Iterator<OMElement> iter = as.getChildElements();
while(iter.hasNext()){
OMElement attribute = iter.next();
if(attribute.getAttributeValue(friendlyName).equalsIgnoreCase(MediatorConstants.QOS_CLIENT_ROLE)){
result = attribute.getFirstElement().getText();
}
}
}
return result;
}
@SuppressWarnings("unchecked")
private String getService(MessageContext ctx){
String result = "";
OMElement subjectConf = getSAMLSubjectConformation(getSAMLAssertion(ctx.getEnvelope()));
if(subjectConf != null){
Iterator<OMElement> iter = subjectConf.getChildElements();
while(iter.hasNext()){
OMElement attribute = iter.next();
result = attribute.getAttributeValue(recipient);
break;
}
}
if(result != null && !result.isEmpty()){
String[] res = endpointPattern.split(result);
if(res.length > 1){
result = res[1];
}
}
return result;
}
@Override
protected String getName() {
return "SAMLMediator";
}
public boolean isDetachAssertion() {
return detachAssertion;
}
public void setDetachAssertion(boolean detachAssertion) {
this.detachAssertion = detachAssertion;
}
}