MashZone NextGen 10.2 | Appendix | Legacy Presto components | Mashables and Mashups | Mashables | Connect Information Sources as Mashables | Configure Secure Connections for Mashables | Implement a Custom Security Profile Client
 
Implement a Custom Security Profile Client
Custom security profiles use HTTP or HTTPS to connect to the mashable. They are implemented in Java, using the MashZone NextGen Security Profile API and consist of these components:
*Security Profile = an object with the credentials or other security information that users provide when they register a mashable.
*Service Invocation Context = context information for the specific request to MashZone NextGen to invoke this mashable.
*Security Profile Client = the client class that handles making secure connections, constructing credentials or other security information and sending requests with this security information to mashables that use this type of profile.
When mashables are invoked, MashZone NextGen instantiates both the security profile and the service invocation context based on saved metadata from the MashZone NextGen Repository and the request to MashZone NextGen to invoke the mashable. To implement the security profile client, you:
1. Add the following JAR file to your classpath to include the Security Profile API in your development environment:
web-apps-home/mashzone/WEB-INF/lib/presto-common.jar
2. Import API Classes and Extend BaseSecureServiceClient
3. Implement the getData Method
4. Implement the Remaining SecureServiceClient Methods
Once you have implemented the custom security profile client, a MashZone NextGen administrator must Register a Custom Security Profile in MashZone NextGen to allow users to select this profile when they register mashables.
Import API Classes and Extend BaseSecureServiceClient
In your custom security profile client class, import the following classes:
package com.myorg.security.service.profile
...
//import com.jackbe.jbp.common.httpclient.SecureServiceClient;
import com.jackbe.jbp.common.httpclient.BaseSecureServiceClient;
import com.jackbe.jbp.common.httpclient.SecureServiceClientException;
import com.jackbe.jbp.common.plugin.ServiceInvocationContext;
import com.jackbe.jbp.common.security.profile.SecurityProfile;
...
Note: BaseSecureServiceClient implements the SecureServiceClient interface, so only one of these import statements is needed.
Have your client class extend BaseSecureServiceClient or implement the SecureServiceClient interface:
package com.myorg.security.service.profile
...
//import com.jackbe.jbp.common.httpclient.SecureServiceClient;
import com.jackbe.jbp.common.httpclient.BaseSecureServiceClient;
import com.jackbe.jbp.common.httpclient.SecureServiceClientException;
import com.jackbe.jbp.common.plugin.ServiceInvocationContext;
import com.jackbe.jbp.common.security.profile.SecurityProfile;
...
public class MyCustomSecureClient extends BaseSecureServiceClient {
...
}
The BaseSecureServiceClient class includes convenience methods to:
*Get the MashZone NextGen default HTTP client (Apache)
*Use the configured proxy server, if any, for MashZone NextGen
*Convert HTTP headers from the mashable response to a Map which allows them to be easily forwarded in the MashZone NextGen response
Implement the getData Method
You must implement the getData(java.lang.String requestData, ServiceInvocationContext invCtx) method to actually invoke the mashable. This method is also responsible for using the credentials or other security information from the security profile to build a request in the form the mashable expects and to handle any other security requirements for the connection to the mashable.
The requestData input parameter for this method supplies the body of the request that MashZone NextGen has built to send to this mashable. The invCtx parameter is the ServiceInvocationContext instance for this invocation. This also contains the SecurityProfile for this mashable.
A very common flow for getData is to:
1. Get an HTTP client using the getDefaultHTTPClient method.
public InputStream getData(String requestData, ServiceInvocationContext invCtx) throws SecureServiceClientException {
String username="";
String password="";
String token="";

logger.debug("...invoking using SampleSecureServiceClient...");
try {
httpClient = getDefaultHTTPClient();
...

} catch(HttpException ex) {
logger.debug("http error...", ex);
throw new SecureServiceClientException("http error", ex);
} catch(IOException ex) {
logger.debug("io error...", ex);
throw new SecureServiceClientException("io error", ex);
} catch(Exception ex) {
logger.debug("error...", ex);
throw new SecureServiceClientException("error", ex);
}
}
2. Get the security parameter values that were defined when the mashable was registered from the SecurityProfile in ServiceInvocationContext:
...
try {
httpClient = getDefaultHTTPClient();

//get parameters from security profile
SecurityProfile secProfile = invCtx.getSecurityProfile();
if(secProfile!=null) {
Map profileParams = secProfile.getParams();
for(Iterator itr=profileParams.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry<String, String>)itr.next();
if (((String)entry.getKey()).equals("username")) {
username = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("pwd")) {
password = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("token")) {
token = (String)entry.getValue();
}
}
}
...
}
}
3. Then build the credentials and other security information into the form expected by the mashable and add this to the message.
...
try {
httpClient = getDefaultHTTPClient();

//get parameters from security profile
SecurityProfile secProfile = invCtx.getSecurityProfile();
if(secProfile!=null) {
Map profileParams = secProfile.getParams();
for(Iterator itr=profileParams.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry<String, String>)itr.next();
if (((String)entry.getKey()).equals("username")) {
username = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("pwd")) {
password = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("token")) {
token = (String)entry.getValue();
}
}
}

if (token==null || token.trim().length()=0) {
logger.error("no token in security profile");
} else {
//build credentials in correct form and add to request
}
...
}
}
4. Determine the HTTP method to use for this request. This is based on the invocation type in the ServiceInvocationContext:
...
if (token==null || token.trim().length()=0) {
logger.error("no token in security profile");
} else {
//build credentials in correct form and add to request
}

//get HTTP method based on invocation type
String invType = invCtx.getInvocationType();
if(invType==null || invType.trim().length()==0)
invType = ServiceInvocationContext.INVOCATION_TYPE_GET;
if(invType.equals(ServiceInvocationContext.INVOCATION_TYPE_GET)) {
httpMethod = new GetMethod(requestData);
} else if(invCtx.getInvocationType().equals(ServiceInvocationContext.INVOCATION_TYPE_POST)) {
httpMethod = new PostMethod(invCtx.getServiceURL());
((PostMethod)httpMethod).setRequestBody(requestData);
}
...
}
}
5. Make sure the client can use the proxy server if one has been configured for MashZone NextGen. This uses the setupHttpProxy convenience method from BaseSecureServiceClient:
...
//get HTTP method based on invocation type
String invType = invCtx.getInvocationType();
if(invType==null || invType.trim().length()==0)
invType = ServiceInvocationContext.INVOCATION_TYPE_GET;
if(invType.equals(ServiceInvocationContext.INVOCATION_TYPE_GET)) {
httpMethod = new GetMethod(requestData);
} else if(invCtx.getInvocationType().equals(ServiceInvocationContext.INVOCATION_TYPE_POST)) {
httpMethod = new PostMethod(invCtx.getServiceURL());
((PostMethod)httpMethod).setRequestBody(requestData);
}
// setup http proxy if required
setupHttpProxy(httpClient, httpMethod);
...

} catch(HttpException ex) {
logger.debug("http error...", ex);
throw new SecureServiceClientException("http error", ex);
} catch(IOException ex) {
logger.debug("io error...", ex);
throw new SecureServiceClientException("io error", ex);
} catch(Exception ex) {
logger.debug("error...", ex);
throw new SecureServiceClientException("error", ex);
}
}
6. Invoke the mashable and return the response:
...
// setup http proxy if required
setupHttpProxy(httpClient, httpMethod);

// invoke
httpClient.executeMethod(httpMethod);

// return data as stream
return httpMethod.getResponseBodyAsStream();
} catch(HttpException ex) {
logger.debug("http error...", ex);
throw new SecureServiceClientException("http error", ex);
} catch(IOException ex) {
logger.debug("io error...", ex);
throw new SecureServiceClientException("io error", ex);
} catch(Exception ex) {
logger.debug("error...", ex);
throw new SecureServiceClientException("error", ex);
}
}
The following example, however, shows getData that does not use the default HTTP client. This example is for a security profile for WSDL mashables that require a WSS Policy using the Oasis User Token Profile. It gets the security profile from the invCtx parameter and uses this data to build the SOAP header for the WSS Policy. It then constructs the SOAP message using the SOAP body passed in requestData.
@Override
public InputStream getData(String soapBodyStr, ServiceInvocationContext invCtx) throws SecureServiceClientException
{
String username = "";
String password = "";
String soapAction = "";
String endpoint = "";
SoapVersion soapVersion = SoapVersion.Soap11;

//get security profile data
SecurityProfile secProfile = invCtx.getSecurityProfile();
if(secProfile!=null) {
Map<String,String> profileParams = secProfile.getParams();
for(Iterator itr=profileParams.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry<String, String>)itr.next();
if (((String)entry.getKey()).equals("username")) {
username = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("pwd")) {
password = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("soapAction")) {
soapAction = (String)entry.getValue();
}
else if (((String)entry.getKey()).equals("soapVersion")) {
if (((String)entry.getValue()).equals("1.1"))
soapVersion = SoapVersion.Soap11;
else if (((String)entry.getValue()).equals("1.2"))
soapVersion = SoapVersion.Soap12;
}
else if (((String)entry.getKey()).equals("endpoint")) {
endpoint = (String)entry.getValue();
}
}
}


byte[] nonceValue = null;
try {
nonceValue = WSSecurityUtil.generateNonce(16);
}
catch (WSSecurityException ex) {
}

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

String soapNamespace = "";

try {
XmlObject soapBody = XmlObject.Factory.parse(soapBodyStr);
XmlCursor cursor = soapBody.newCursor();

if (soapVersion == SoapVersion.Soap12) {
soapNamespace = SOAP_12_NS;
cursor.selectPath("declare namespace soap='" + soapNamespace + "';./soap:Envelope/soap:Body");
}
else if (soapVersion == SoapVersion.Soap11) {
soapNamespace = SOAP_11_NS;
cursor.selectPath("declare namespace soap='" + soapNamespace + "';./soap:Envelope/soap:Body");
}
else {
logger.error("OASIS User-Token-Profile 1.0, invalid soap version set to " + soapVersion);
}

//build SOAP header for WSS policy token
String UTPSOAPHeader =
"<soap:Header xmlns:soap=\"" + soapNamespace + "\">" +
"<wsse:Security soapenv:mustUnderstand=\"1\"
xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">" +
"<wsse:UsernameToken wsu:Id=\"UsernameToken-1\"
xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
"<wsse:Username>" + username + "</wsse:Username>" +
"<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">"
+ password + "</wsse:Password>" +
"<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">"
+ Base64.encode(nonceValue) + "</wsse:Nonce>" +
"<wsu:Created>" + dateFormat.format(new Date()) + "</wsu:Created>" +
"</wsse:UsernameToken>" +
"</wsse:Security>" +
"</soap:Header>";

XmlObject soapHeader = XmlObject.Factory.parse(UTPSOAPHeader);
XmlObject inputMessage = null;

if (cursor.hasNextSelection() && cursor.toNextSelection()) {
do {
cursor.toNextToken();
}
while(!cursor.isContainer());

inputMessage = XmlObject.Factory.parse(cursor.xmlText());
}

//build full SOAP request
XmlObject soapRequest = SoapMessageGenerator.buildSoapRequest(false, soapHeader, inputMessage, soapVersion);
WSSecUsernameToken usernameToken = new WSSecUsernameToken();
SoapClient soapClient = new SoapClient();

if (soapAction != null) {
if (soapAction.length() == 0)
soapClient.setSoapAction("\"\"");
else
soapClient.setSoapAction("\"" + soapAction + "\"");
}

if (endpoint != null && endpoint.length() > 0)
soapClient.setEndpointRef(endpoint);
else
soapClient.setEndpointRef(invCtx.getServiceURL());

//invoke service
soapClient.setSoapVersion(soapVersion);
soapClient.loadSoapRequestFromXmlObject(soapRequest);
System.out.println(soapRequest.xmlText());
XmlObject responseXml = soapClient.invoke();
return WsdlUtils.fromString(responseXml.xmlText());
}
catch(XmlException ex) {
logger.error("fail parsing soap header or body: " + ex.getMessage(), ex);
}
catch(SoapClientException ex) {
logger.error("fail invoking the service: " + ex.getMessage(), ex);
}

return null;
}
Implement the Remaining SecureServiceClient Methods
You must implement the remaining SecureServiceClient methods, although they may not perform any operations depending on the requirements for this security profile:
*getMetaData(java.lang.String requestData, ServiceInvocationContext invCtx): this method is used when mashable information sources are registered with this security profile. Typically, this also invokes the service, just as getData does. For example:
public InputStream getMetadata(String requestData, ServiceInvocationContext invCtx) throws SecureServiceClientException {
// no unique requirements, just call getData
return getData(requestData, invCtx);
}
*getResponseHeaders(): to retrieve HTTP headers from the mashable’s response. This is most commonly used to allow headers to be included in the MashZone NextGen response. For example:
public Map<String,String> getResponseHeaders() {
Header[] headers = httpMethod.getResponseHeaders();
Map<String,String> responseHeaders = convertHeadersToMap(headers);
return responseHeaders;
}
This example uses the convertHeadersToMap convenience method from BaseSecureServiceClient to convert the array of HTTP headers to a Map so that MashZone NextGen can include them in the response.
*getStatusCode(): to retrieve the HTTP status code from the mashable’s response. For example:
public int getStatusCode() {
return httpMethod.getStatusCode();
}
*getStatusText(): to retrieve the HTTP status text from the mashable’s response. For example:
public String getStatusText() {
return httpMethod.getStatusText();
}
*close(): to explicitly close the connection. For example:
public void close() {
if(httpMethod!=null)
httpMethod.releaseConnection();
}

Copyright © 2013-2018 | Software AG, Darmstadt, Germany and/or Software AG USA, Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.
Innovation Release