Sunday, September 21, 2014

ESB: Invoking secured backend - Part 3 - Username Token with BasicAuth

This post shows, how to invoke an UsernameToken secured backend ( Hosted in WSO2 AS ), using basic auth. For this we use POXSecurityHandler, (Which comes default with WSO2 Products) to convert the HTTP basic auth information into wsse:UsernameToken.

Setting up environment : 

Setup both WSO2 AS and WSO2 ESB as mentioned in previous post.


ESB Proxy
  • Create a proxy called EchoUTBasicProxy with following content. 
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="EchoUTBasicProxy"
transports="https,http"
statistics="disable"
trace="disable"
startOnLoad="true">
<target>
<inSequence>
<log level="custom" separator="," description="Log">
<property name="Proxy" value="EchoUTWithBasicAuthProxy"/>
</log>
<property name="user_name" value="bob" scope="default" type="STRING"/>
<property xmlns:ns="http://org.apache.synapse/xsd"
name="Authorization"
expression="fn:concat('Basic ', base64Encode(fn:concat(get-property('user_name'),':','bobpass')))"
scope="transport"
type="STRING"/>
<log level="custom">
<property name="Generated Authorization token" expression="$trp:Authorization"/>
</log>
<send>
<endpoint>
<address uri="https://localhost:9443/services/echo"/>
</endpoint>
</send>
</inSequence>
<outSequence>
<log level="full"/>
<header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
name="wsse:Security"
scope="default"
action="remove"/>
<send/>
</outSequence>
<faultSequence/>
</target>
<publishWSDL uri="http://localhost:9763/services/echo?wsdl"/>
<description/>
</proxy>

Testing Scenario
  • Enable Soap tracer on WSO2 AS.
  • Enable wire log in WSO2 ESB.
  • Invoke EchoUTBasicProxy  using SOAP UI. 
You can see, there is no username token in incoming message to backend. Instead you see basic auth header in outgoing message to backend from ESB.




ESB: Invoking secured backend - Part 2 - Username Token - Dynamic username

My previous post shows how to invoke an username token secured backend using an ESB proxy. But we used static value for the username ( tom ), which is hard coded in the policy file. So each request authenticated as tom at the backend service.

But some may wants to access backend service as different users. This post discusses how you can extend it to support dynamic user name in policy file.


Setting up environment : 

Setup both WSO2 AS and WSO2 ESB as mentioned in previous post. 


ClassMediator (ESB)
  • In this scenario, we set username as a property in the ESB proxy. 
  • To pass username into RampartConfiguration, we use custom class mediator called, SetUserMediator.
  • This custom mediator, adds username into rampartConfigCallbackProperties map and set the map into Axis2MessageContext. So later we can access these properties from Rampart ConfigCallbackHandlers. 
  • We have to use customMediator, since we can't set a Map using standard ESB mediators.  
  • ( Maven Project is located here. )
package org.example.rampart.mediator;
import java.util.Hashtable;
import java.util.Map;
import org.apache.synapse.Mediator;
import org.apache.synapse.MessageContext;
import org.apache.synapse.core.axis2.Axis2MessageContext;
public class SetUserMediator implements Mediator {
private String username;
@Override
public boolean mediate(MessageContext synCtx) {
org.apache.axis2.context.MessageContext msgContext = ((Axis2MessageContext) synCtx)
.getAxis2MessageContext();
Map<String, String> rampConfigCBProperties = new Hashtable<String, String>();
System.out.println("Setting username to rampartConfigCallbackProperties: " + getUsername());
rampConfigCBProperties.put("user_name", getUsername());
msgContext.setProperty("rampartConfigCallbackProperties",
rampConfigCBProperties);
return true;
}
public String getUsername() {
if (username == null) {
username = "";
}
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getDescription() {return null;}
@Override
public void setDescription(String arg0) {}
@Override
public int getMediatorPosition() {return 0; }
@Override
public String getShortDescription() {return null; }
@Override
public int getTraceState() {return 0; }
@Override
public String getType() {return null; }
@Override
public boolean isContentAware() {return false; }
@Override
public void setMediatorPosition(int arg0) {}
@Override
public void setShortDescription(String arg0) {}
@Override
public void setTraceState(int arg0) {}
}


Rampart ConfigCallbackHandler (ESB)
  • Similar to PasswordCallback handler, Rampart provides Configuration Callback handler to dynamically load Rampart configuaraion to runtime. We use this to set username dynamically. 
  • ( Maven Project is located here. )

package org.example.rampart;
import java.util.Map;
import org.apache.rampart.RampartConfigCallbackHandler;
import org.apache.rampart.policy.model.RampartConfig;
public class UTConfigCallbackHandler implements RampartConfigCallbackHandler{
@Override
public void update(RampartConfig rampartConfig) {
Map<String, String> propertyMap = rampartConfig.getPropertyMap();
if (propertyMap != null && propertyMap.containsKey("user_name")) {
System.out.println("Updating UT user name dynamically : " + propertyMap.get("user_name"));
rampartConfig.setUser(propertyMap.get("user_name"));
}
rampartConfig.setPwCbClass("org.example.rampart.PWCBHandler");
}
}

Policy for UsernameToken  ( ESB )
  • Create an ESB in-line xml local entry called "UTOverTransportDynamic.xml" with following content. 

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UTOverTransport">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate="false"></sp:HttpsToken>
</wsp:Policy>
</sp:TransportToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256></sp:Basic256>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax></sp:Lax>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp></sp:IncludeTimestamp>
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"></sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
<ramp:rampartConfigCallbackClass>org.example.rampart.UTConfigCallbackHandler</ramp:rampartConfigCallbackClass>
</ramp:RampartConfig>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>

Proxy Service (ESB)

  • Create a proxy called EchoUTDynamicProxy with following content. 
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="EchoUTDynamicProxy"
transports="https,http"
statistics="disable"
trace="disable"
startOnLoad="true">
<target>
<inSequence>
<log level="full"/>
<class name="org.example.rampart.mediator.SetUserMediator">
<property name="username" value="bob"/>
</class>
<send>
<endpoint>
<address uri="https://localhost:9443/services/echo">
<enableSec policy="UTOverTransportDynamic.xml"/>
</address>
</endpoint>
</send>
</inSequence>
<outSequence>
<log level="full"/>
<header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
name="wsse:Security"
scope="default"
action="remove"/>
<send/>
</outSequence>
</target>
<publishWSDL uri="http://localhost:9763/services/echo?wsdl"/>
<description/>
</proxy>

Testing Scenario

  • Enable Soap tracer on WSO2 AS.
  • Invoke EchoUTDynamicProxy  using SOAP UI. 
You can see Username token in request message as follows.


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:echo="http://echo.services.core.carbon.wso2.org">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-7">
<wsu:Created>2014-09-21T06:38:53.795Z</wsu:Created>
<wsu:Expires>2014-09-21T06:43:53.795Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-8">
<wsse:Username>bob</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">bobpass</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<echo:echoString>
<!--Optional:-->
<in>?</in>
</echo:echoString>
</soapenv:Body>
</soapenv:Envelope>
view raw gistfile1.xml hosted with ❤ by GitHub

ESB: Invoking secured backend - Part 1 - Username Token



Scenario 
  1. Backend service is secured using Username token. 
  2. Client invokes ESB proxy using http. ( no security between client and ESB) 
  3. At the ESB, proxy adds username token to outgoing message and invokes secured backend.
  4. ESB sends back echo service's response back to client. 

Setting up environment 

Backend ( WSO2 Application server 5.2.1)
  1. Start WSO2 AS 5.2.1 using ( Unix: sh wso2server.sh / Windows: wso2server.bat ) 
  2. Log in to management console. ( https://localhost:9443/carbon/ ) 
  3. Create two user called tom and bom
    • Goto Configure -> Users and Roles -> Users
    • Create an user called tom with password "tompass". 
    • Create another user called bob with password "bobpass"
    • Assign both users to "admin" role.
  4. Secure Echo service with Username token. 
    • Goto Main -> Services -> List 
    • Click on "echo" service. This will open up "Service Dashboard (echo)" page.
    • Under "Quality of Service Configuration", Select "security".
    • In "Security for the service" page, Select Enable security.
    • Under Security scenarios, select "Username token"  ( First security policy) and click next. 
    • In next page, select "admin" under user group. 
    • Click Finish. 
ESB ( WSO2 ESB 4.8.1 )
  1. Start WSO2 ESB with port offset =1 ( Unix: sh wso2server.sh -DportOffset=1 / Windows: wso2server.bat --DportOffset=1) 


Rampart configuration for UsernameToken  ( ESB )
  • Create an ESB in-line xml local entry called "UTOverTransport.xml" with following content. 
<wsp:Policy wsu:Id="UTOverTransport"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:TransportBinding
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate="false" />
</wsp:Policy>
</sp:TransportToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
<sp:SignedSupportingTokens
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient" />
</wsp:Policy>
</sp:SignedSupportingTokens>
<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
<ramp:user>tom</ramp:user>
<ramp:passwordCallbackClass>org.example.rampart.PWCBHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>

Password callback Implementation

  • Create a jar with following class, and drop it to /repository/components/lib/
  • Then restart ESB server. 
  • ( Maven Project is located here. )
package org.example.rampart;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
import java.io.IOException;
public class PWCBHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[i];
int usage = pwcb.getUsage();
String id = pwcb.getIdentifier();
if (usage == WSPasswordCallback.USERNAME_TOKEN) {
System.out.println("Resolving password for user " + id);
// Getting password
if ("tom".equals(id)) {
pwcb.setPassword("tompass");
}else if ("bob".equals(id)){
pwcb.setPassword("bobpass");
} else {
pwcb.setPassword("");
}
} else if (usage == WSPasswordCallback.SIGNATURE
|| usage == WSPasswordCallback.DECRYPT) {
// Logic to get the private key password for signature or
// decryption
// TODO : Implement me
}
}
}
}
Some useful References on Rampart password callback handler:  
  1. http://wso2.com/library/3733/
  2. http://wso2.com/library/240/

ESB Proxy

  • Create a proxy called EchoUTProxy with following content. 
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="EchoUTProxy"
transports="https,http"
statistics="disable"
trace="disable"
startOnLoad="true">
<target>
<inSequence>
<send>
<endpoint>
<address uri="https://localhost:9443/services/echo">
<enableSec policy="UTOverTransport.xml"/>
</address>
</endpoint>
</send>
</inSequence>
<outSequence>
<log level="full"/>
<header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
name="wsse:Security"
scope="default"
action="remove"/>
<send/>
</outSequence>
</target>
<publishWSDL uri="http://localhost:9763/services/echo?wsdl"/>
<description/>
</proxy>
view raw EchoUTProxy.xml hosted with ❤ by GitHub


Testing Scenario

  • Enable Soap tracer on WSO2 AS.
  • Invoke EchoUTProxy  using SOAP UI. 
You can see Username token in request message as follows. 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:echo="http://echo.services.core.carbon.wso2.org">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-13">
<wsu:Created>2014-09-21T04:17:56.541Z</wsu:Created>
<wsu:Expires>2014-09-21T04:22:56.541Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-14">
<wsse:Username>tom</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">tompass</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<echo:echoString>
<!--Optional:-->
<in>?</in>
</echo:echoString>
</soapenv:Body>
</soapenv:Envelope>
view raw gistfile1.xml hosted with ❤ by GitHub