Sunday, November 30, 2008

Open source: The safe goddess of economic crisis

With the global economy slowing to a crawl, spending hundreds of thousands of dollars on SOA solutions from the large platform vendors can be hard to justify.

Fortunately, the availability of proven open source SOA software means IT departments can continue to move full speed ahead with their SOA implementations.

More details available here.

Saturday, November 29, 2008

Gaining control over Axis2 service creation

By default Axis2 engine will instantiate your service implementation based on it's scope.

At the same time it has a mechanism to delegate the service creation functionality to the service developer him self.

So, then - it's you who decide how to instantiate your service implementation.

1. You need to have a class having a method with the signature public static Object getServiceObject(AxisService service).

2. Need to add the parameter 'ServiceObjectSupplier' to the services.xml, pointing to the above class having the given method signature.

<service name="SimpleService" scope="application">
<operation name="echo">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<!-- parameter name="ServiceClass" locked="false">org.apache.ws.axis2.SimpleService</parameter -->
<parameter name="ServiceObjectSupplier" locked="false">org.apache.ws.axis2.SimpleServiceSupplier</parameter>
</service>

package org.apache.ws.axis2;

import org.apache.axis2.description.AxisService;

public class SimpleServiceSupplier {

public static SimpleService getServiceObject(AxisService service) {
return new SimpleService();
}
}

Friday, November 28, 2008

Axis2 services : LifeCycle Vs ServiceLifeCycle

Both the LifeCycle and the ServiceLifeCycle interfaces give the service developer additional control over service's life cycle.

LifeCycle interface introduces two methods, init(ServiceContext serviceContext) and destroy(ServiceContext serviceContext).

init() will be fired at the start of the session and destroy will be fired at the time the SessionContext object being garbage collected.

In the case of 'application' scope, if the service being in the repository before axis2 boots up - then init() method will be called during axis2 engine startup - if the service is deployed after axis2 engine started up, then init() method will be called at the time of the first request to the service.

To get access to these two methods you simply need to add them to your service implementation class.

Even you do not implement the LifeCycle interface - but have the above two methods in your service implementation - still your methods will be called by the axis2 engine. But it's highly recommended that - if you need access to these methods, you better keep the LifeCycle interface implemented.

In the case of ServiceLifeCycle interface - it introduces two methods startUp(ConfigurationContext configctx, AxisService service) and shutDown(ConfigurationContext configctx, AxisService service).

startUp() will be fired during the deployment time of the service and shutDown() will be fired during the system shut down time, irrespective of the service scope.

To get access to these two methods you need to do two things.

1. Keep ServiceLifeCycle interface implemented.
2. Add 'class' attribute to the <service> element in the services.xml and point it to the ServiceLifeCycle implementation.

Let me revisit one point that I mentioned, before.

If the service is deployed after axis2 engine started up, then LifeCyle interface's init() method will be called at the time of the first request to the service - but even in this case startUp() of ServiceLifeCycle will be called as soon as service got deployed.

Another difference is, you can have ServiceLifeCycle implementation apart from your service implementation - but in the case of LifeCycle , both should be the same.

<service name="SimpleService" class="org.apache.ws.axis2.SimpleService" scope="application">
<operation name="echo">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<parameter name="ServiceClass" locked="false">org.apache.ws.axis2.SimpleService</parameter>
</service>

package org.apache.ws.axis2;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.engine.ServiceLifeCycle;
import org.apache.axis2.service.Lifecycle;

/**
* The service implementation class
*/
public class SimpleService implements ServiceLifeCycle, Lifecycle {

/**
* This is called when a new instance of the implementing class has been created.
* This occurs in sync with session/ServiceContext creation. This method gives classes
* a chance to do any setup work (grab resources, establish connections, etc) before
* they are invoked by a service request.
*/
public void init(ServiceContext serviceContext) throws AxisFault {
System.out.println("Lifecycle:::init()");

}

/**
* This is called when Axis2 decides that it is finished with a particular instance
* of the back-end service class. It allows classes to clean up resources.
*/
public void destroy(ServiceContext serviceContext) {
System.out.println("Lifecycle:::destroy()");
}

/**
* This will be called during the deployment time of the service.
* Irrespective of the service scope this method will be called
*/
public void startUp(ConfigurationContext configctx, AxisService service) {
System.out.println("ServiceLifeCycle:::startUp()");
}

/**
* This will be called during the system shut down time. Irrespective
* of the service scope this method will be called
*/
public void shutDown(ConfigurationContext configctx, AxisService service) {
System.out.println("ServiceLifeCycle:::shutDown()");

}

/**
* The echo method which will be exposed as the echo operation of the web
* service
*/
public String echo(String value) {
return value;
}

}

Thursday, November 27, 2008

Carbon: What you KNOW and do NOT KNOW

This is all about everything you know about Carbon.

AND... this is what you did NOT know...

Wednesday, November 26, 2008

Reading SSL certificates from a given url

This code explains how you could read and retrieve an X.509 certificate from a given url.

import java.net.URL;
import java.security.cert.Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public static Certificate readSSLCertFromUrl(String url) throws Exception {
URL hostURL = null;
String hostname = null;
int port;
SSLSocketFactory factory = null;
SSLSocket socket = null;

try {
hostURL = new URL(url);
hostname = hostURL.getHost();

// Check whether the url has a port stated explicitly. If its not present default to 443
port = hostURL.getPort();
if (port == -1) {
port = 443;
}

// Gets the default static SSLSocketFactory that is inherited by new instances of this
// class.
// The socket factories are used when creating sockets for secure https URL connections.
factory = HttpsURLConnection.getDefaultSSLSocketFactory();

// Creates a socket and connects it to the specified remote host at the specified remote
// port. This socket is configured using the socket options established for this
// factory.
socket = (SSLSocket) factory.createSocket(hostname, port);

// Starts an SSL handshake on this connection. Common reasons include a need to use new
// encryption keys, to change cipher suites, or to initiate a new session. To force
// complete reauthentication, the current session could be invalidated before starting
// this handshake.
socket.startHandshake();

// Retrieve the server's certificate chain
Certificate[] serverCerts = socket.getSession().getPeerCertificates();

// The local certificate first followed by any certificate authorities.
if (serverCerts != null && serverCerts.length > 0) {
return serverCerts[0];
} else {
return null;
}

} finally {
// Close the socket
if (socket != null) {
socket.close();
}
}
}

Tuesday, November 25, 2008

A feature rich open source identity provider

WSO2 Identity Solution is an open source identity management solution with CardSpace and OpenID authentication.

Here I would like to list some of it's features.

Ability to issue managed information cards backed by user name/password and self issued information cards

A registered user can download a managed information card against his account and it can be used at trusted information card relying parties.

A user can totally eliminate the use of username/password by backing his managed information card with a self-issued card.

Supports SAML versions 2.0/1.1

Ability to revoke information cards

Provides the ability to revoke issued information cards and block those from being used to authenticate.

Support for the CardSpace default claim set

Claims are used by relying party web applications to express required user information.

The identity provider by default supports the standard set of claims as defined by CardSpace specifications.

These claims can be mapped to various attributes of users in the user store.

Support for custom claim dialects and claims

Provides the flexibility to define custom claims using the identity provider management console and map those claims to user attributes.

Ability to issue OpenIDs

A registered user will be assigned an OpenID and this OpenID can be used at any OpenID relying party web site.

Support for OpenID 2.0

OpenID 2.0 addresses many security vulnerabilities found in 1.1.

Identity Solution has inbuilt support for OpenID 2.0 with following specifications.

- OpenID Authentication 2.0
- OpenID Authentication 1.1
- OpenID Simple Registration Extension 1.1
- OpenID Attribute Exchange 1.0
- OpenID Provider Authentication Policy Extension 1.0, draft 1
- OpenID Information Cards 1.0 - Draft 01

Multi-factor authentication

Identity Solution's OpenID Provider enables multi-factor authentication for OpenID users to provide a stronger form of authentication.

Support for user white-listing & black-listing

Provides the ability to white/black list information cards and OpenID relying parties.

Ability to connect to custom user stores

Identity Solution can be easily customized to connect to a custom user repository [e.g.: Active Directory] to enable information card and OpenID authentication for users.

Built-in user store

Identity Solution ships with a built-in user store, so it can be used out-of the box to enable information card and OpenID authentication.

Statistics/reporting/audit trail

The Identity Provider management console provides a set of statistics/reports for administrators to monitor user accounts, information card/OpenID issuances and token issuances for login requests to relying party web applications.

Information card relying party support for Apache2

With Apache HTTPD module (mod_cspace) any server side scripting language
supported by Apache2 can enable information card relying party support.

Information card/OpenID relying party support with the Java servlet filter

Provides an easy integration point for J2EE based web applications.

Information card relying party support for leading content management frameworks

Identity Solution includes authentication modules for popular content management frameworks such as Drupal and MediaWiki.

Monday, November 24, 2008

Google moves towards sso with OpenID

As per this announcement by Google, now all your Google accounts are OpenID enabled.

In other words - Google acts as an OpenID Provider - you may recall that even your Blogger urls are OpenID enabled.

Google's support to OpenID is through the OpenID 2.0 Directed Identity protocol.

Directed Identity is a term introduced with the seven laws of Identity and it says,

"A universal identity system must support both ‘Omni-directional’ identifiers for use by public entities and ‘unidirectional’ identifiers for use by private entities, thus facilitating discovery while preventing un-necessary release of correlation handles."

Under the OpenID terminology 'Omni-directional’ identifier is equivalent to the OP-Local identifier.

With Google, you can login to any OpenID RP by entering Google OP-Local Identifier, that is https://www.google.com/accounts/o8/id.

Sunday, November 23, 2008

Google OAuth Playground

This demonstrates how an OAuth client can access your GMail account through the OAuth Protocol.

In our case http://googlecodesamples.com/oauth_playground is the OAuth consumer app and GMail is the service provider.

OAuth consumer needs to be pre-registered at the service provider - let's keep the OAuth parameters as they are.

With OAuth, there are two types of tokens used: request tokens and access tokens.

Request tokens are used to verify consumer registration with the service provider and get end user authorization, after which they can be exchanged for an access token.

First - OAuth consumer needs to request a 'request token'. As a response to this, the service provider will return an unauthorized request token.

Then the consumer will redirect the user to the service provider - and he can authorize the request. [After the step 4, you'll get to the image one before the last]

Once user authorized the request, consumer gets an authorized request token.

Now, in step-5, consumer exchanges the authorized request token for an access token.

The access token received in response to this request is used to request data from the service provider.

Saturday, November 22, 2008

OpenID Authenticator for Tomcat

By default Tomcat supports following auth-method's with corresponding Authenticators.

1. BASIC - org.apache.catalina.authenticator.BasicAuthenticator
2. FORM - org.apache.catalina.authenticator.FormAuthenticator
3. DIGEST - org.apache.catalina.authenticator.DigestAuthenticator
4. CLIENT-CERT - org.apache.catalina.authenticator.SSLAuthenticator

My previous post explains how BASIC authentication works with Tomcat.

In this post, we'll be adding a new type of Authenticator to Tomcat.

5. OPENID - org.wso2.OpenIDAuthenticator

With this you can protect your web resources with OpenID authentication.

I am using WSO2 OpenID Relying Party components which do ship with WSO2 Identity Solution.

Let's get started.

First we need to configure Tomcat to use our custom Authenticator.

Extract [CATALINA_HOME]\server\lib\catalina.jar and edit the file \org\apache\catalina\startup\Authenticators.properties to look like following - simply adding our Authenticator to it.
# These must match the allowed values for auth-method as defined by the spec 
BASIC=org.apache.catalina.authenticator.BasicAuthenticator
CLIENT-CERT=org.apache.catalina.authenticator.SSLAuthenticator
DIGEST=org.apache.catalina.authenticator.DigestAuthenticator
FORM=org.apache.catalina.authenticator.FormAuthenticator
NONE=org.apache.catalina.authenticator.NonLoginAuthenticator
OPENID=org.wso2.OpenIDAuthenticator
Now we need to re-pack the extracted jar with our change to catalina.jar and keep it in it's original location.

You can download other dependency jars from here.

Copy the jars inside [ZIP_FILE]\jars to [CATALINA_HOME]\server\lib and the jars from [ZIP_FILE]\endorsed to [CATALINA_HOME]\common\endorsed.

That's it with Tomcat configuration.

Now, let's see how we can configure OPENID authentication for our webapp.

It's basically the same way you configure BASIC or any other auth-method for your web app.

You can copy [ZIP_FILE]\demo-app folder to [CATALINA_HOME]\webapps.

Let's have a look at [CATALINA_HOME]\webapps\demo-app\WEB-INF\web.xml.
<web-app> 

<security-constraint> 
<web-resource-collection>
<web-resource-name>secured resources</web-resource-name> 
<url-pattern>/web/*</url-pattern> 
</web-resource-collection>
<auth-constraint> 
<role-name>*</role-name> 
</auth-constraint> 
</security-constraint> 

<login-config> 
<auth-method>OPENID</auth-method> 
<form-login-config>
<form-login-page>/openid-login.jsp</form-login-page>
<form-error-page>/denied.jsp</form-error-page>
</form-login-config> 
</login-config> 

</web-app>
Here, the openid-login.jsp and denied.jsp pages are not application specific - so can be reused across.

All - set, let's see how the demo works - you can also access the online demo from here.

http://localhost:8080/demo-app/ - this is not a protected resource.

Click on the link to, Protected Resource - since this is OpenID protected and you are not authenticated yet, you'll be redirected to the OpenID login page - Type your OpenID there and complete the OpenID authentication routine.

You are on the protected resource now...

I'll just dump the code here for the OpenIDAuthenticator and the OpenIDRealm - it's self-explanatory through comments.
package org.wso2;

import java.io.IOException;
import java.security.Principal;

import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.authenticator.FormAuthenticator;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.wso2.solutions.identity.relyingparty.RelyingPartyException;
import org.wso2.solutions.identity.relyingparty.TokenVerifierConstants;
import org.wso2.solutions.identity.relyingparty.openid.OpenIDAuthenticationRequest;
import org.wso2.solutions.identity.relyingparty.openid.OpenIDConsumer;
import org.wso2.solutions.identity.relyingparty.openid.OpenIDRequestType;
import org.wso2.solutions.identity.relyingparty.openid.OpenIDUtil;

/**
* This extends the functionality of FormAuthenticator to facilitate OpenID logins.
* 
* @author Prabath Siriwardena @ WSO2 
* http://www.wso2.org 
* http://blog.facilelogin.com
* 
*/
public class OpenIDAuthenticator extends FormAuthenticator {

/**
* {@inheritDoc}
*/
public boolean authenticate(Request request, Response response, LoginConfig config) {
Principal principal = null;
boolean loginAction = false;
boolean isAuthenticated = false;
String requestURI = null;
Realm realm = null;
String openID = null;

// References to objects we will need later
Session session = null;

principal = request.getUserPrincipal();

if (principal != null) {
// We are here because we have being authenticated successfully, before.
return true;
}

// Check whether this is a re-submit of the original request URI after successful
// authentication? If so, forward the *original* request instead.
if (matchRequest(request)) {
return matchRequest(request, response, config);
}

// This should be the OpenID return to url.
requestURI = request.getDecodedRequestURI();

// This request came from the login page - let me login - here are my credentials.
loginAction = (request.getParameter("login") != null);

if (!loginAction) {
// This can be the initial request for the protected resource or being redirected back
// by the OpenID Provider.

if (OpenIDUtil.isOpenIDAuthetication(request)) {
// This is an OpenID response - follow the OpenID protocol
String auth = null;
try {
OpenIDConsumer.getInstance().setSessionAttributes(request);
auth = (String) request.getAttribute(TokenVerifierConstants.SERVLET_ATTR_STATE);
if (auth != null && TokenVerifierConstants.STATE_SUCCESS.equals(auth)) {
isAuthenticated = true;
} else {
forwardToErrorPage(request, response, config);
return (false);
}
} catch (RelyingPartyException e) {
forwardToErrorPage(request, response, config);
return (false);
}
} else {
try {
session = request.getSessionInternal(true);
saveRequest(request, session);
} catch (IOException ioe) {
return (false);
}
request.getSession().setAttribute("requestURI", requestURI);
forwardToLoginPage(request, response, config);
return (false);
}
}

// You are here, because you came here directly from the openid-login page or you are
// authenticated at OP and redircted back.

if (!isAuthenticated) {
// Let's build the OpenID authentication request.
try {
doOpenIDAuthentication(request, response);
return false;
} catch (RelyingPartyException e) {
forwardToErrorPage(request, response, config);
return (false);
}
}

realm = context.getRealm();
session = request.getSessionInternal(false);

if (!(realm instanceof OpenIDRealm)) {
realm = new OpenIDRealm();
context.setRealm(realm);
}

openID = (String) request.getAttribute("openid_identifier");
principal = realm.authenticate(openID, "");

if (principal == null) {
forwardToErrorPage(request, response, config);
return (false);
}

// Save the authenticated Principal in our session
session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);

// Save the OpenID
session.setNote(Constants.SESS_USERNAME_NOTE, openID);
// We have no password for OpenID
session.setNote(Constants.SESS_PASSWORD_NOTE, "");

// Redirect the user to the original request URI (which will cause
// the original request to be restored)
requestURI = savedRequestURL(session);
try {
response.sendRedirect(response.encodeRedirectURL(requestURI));
} catch (IOException e) {
return (false);
}

return (false);
}

/**
* Performs OpenID authentication
* 
* @param request Request we are processing
* @param response Response we are creating
* @throws RelyingPartyException
*/
protected void doOpenIDAuthentication(Request request, Response response)
throws RelyingPartyException {
OpenIDAuthenticationRequest openIDAuthRequest = null;
openIDAuthRequest = new OpenIDAuthenticationRequest(request, response);

openIDAuthRequest.setOpenIDUrl((String) request.getParameter("openIdUrl"));
openIDAuthRequest.addRequestType(OpenIDRequestType.SIMPLE_REGISTRATION);

if (request.getProtocol().equals("HTTP/1.1")) {
openIDAuthRequest.setReturnUrl("http://" + request.getLocalName() + ":"
+ request.getLocalPort() + request.getSession().getAttribute("requestURI"));
} else {
openIDAuthRequest.setReturnUrl("https://" + request.getLocalName() + ":"
+ request.getLocalPort() + request.getSession().getAttribute("requestURI"));
}

OpenIDConsumer.getInstance().doOpenIDAuthentication(openIDAuthRequest);
}

/**
* Check whether this is a re-submit of the original request URI after successful
* authentication? If so, forward the *original* request instead.
* 
* @param request Request we are processing
* @param response Response we are creating
* @param config Login configuration describing how authentication should be performed
*/
private boolean matchRequest(Request request, Response response, LoginConfig config) {
Session session = null;
Principal principal = null;

session = request.getSessionInternal(true);
principal = (Principal) session.getNote(Constants.FORM_PRINCIPAL_NOTE);
register(request, response, principal, Constants.FORM_METHOD, (String) session
.getNote(Constants.SESS_USERNAME_NOTE), (String) session
.getNote(Constants.SESS_PASSWORD_NOTE));
// If we're caching principals we no longer need the username
// and password in the session, so remove them
if (cache) {
session.removeNote(Constants.SESS_USERNAME_NOTE);
session.removeNote(Constants.SESS_PASSWORD_NOTE);
}
try {
if (restoreRequest(request, session)) {
return (true);
} else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return (false);
}
} catch (IOException e) {
forwardToErrorPage(request, response, config);
return (false);
}
}
}
package org.wso2;

import java.security.Principal;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.realm.RealmBase;

/**
* This extends the functionality of RealmBase to facilitate OpenID logins.
* 
* @author Prabath Siriwardena @ WSO2 
* http://www.wso2.org 
* http://blog.facilelogin.com
* 
*/
public class OpenIDRealm extends RealmBase {

/**
* No passwords for OpenID
*/
protected String getPassword(String openID) {
return "";
}

/**
* {@inheritDoc}
*/
protected Principal getPrincipal(String OpenID) {
return new GenericPrincipal(this, OpenID, "", null, null);
}

/**
* We have no roles associated.
*/
public boolean hasRole(Principal principal, String role) {
return false;
}

/**
* Give this realm required permissions.
*/
public boolean hasResourcePermission(Request request, Response response,
SecurityConstraint[] constraints, Context context) {
return true;
}


/**
* Realm name
*/
protected String getName() {
return "OpenIDRealm";
}

}

Friday, November 21, 2008

User Centric Authentication using WSO2 Identity Solution

WSO2 Identity Solution provides a set of components to enable CardSpace/OpenID authentication on various platforms.

This 3-hour course is designed to provide an understanding of CardSpace/OpenID authentication protocols and how to use Identity Solution components.

The course covers following contents and now open for registration.

- Introduction to Microsoft CardSpace concepts

- Introduction to OpenID

- Enabling a CardSpace/OpenID authentication on sample J2EE web application using WSO2 Identity Solution Java relying party component

- Setting up the Identity Provider with LDAP connecting to an Active Directory

- Setting up the Identity Provider with a custom database with user account details

- Federated Identities with WSO2 Identity Solution

- Using custom claims to enable authorization in web applications

Thursday, November 20, 2008

Information Cards for Active Directory Users

In this article I discuss about an open source solution to enable Information Card logins for Active Directory users.

Wednesday, November 19, 2008

Sanjaya on "Right Middleware for SOA in Health Care"

When there are lots of solutions, choosing the right products to build your software is really challenging.

Identifying the right combination of middleware products to build your SOA is key to its success.

In this article, Sanajaya Karunasena from WSO2, discusses an approach to using non-functional requirements in choosing these products, using SOA in healthcare as an example.

Tuesday, November 18, 2008

WSO2 Synergies: ESB and Registry

Monday, November 17, 2008

OAuth + OpenID + InfoCard

This is an imaginary use case to demonstrate how OAuth,OpenID and InfoCards fit together.

1. User logs into Facebook with his user credentials.

2. User wants to share images from Flickr with Facebook.

3. OAuth comes into act now.

4. Facebook requests Request_Token from Flickr to access user's photos on his behalf.

5. Since user has not authorized the request, Facebook gets unauthorized Request_Token from Flickr.

6. Now, Facebook will redirect the user to Flickr for authentication.

7. User presents his OpenID at Flickr to get authenticated.

8. Flickr redirects the user to his OpenID Provider for authentication - say myOpenID.

9. User authenticates at the myOpenID with a registered Information Card.

10.On successfull login user is redirected back to Flickr.

11.Now, Flickr will ask the user - whether its okay to give Facebook the access to his photos - and once selected 'yes' - user will be redirected back to Facebook.

12.Now, Facebook will request an Access Token from Flickr.

13.Since the user has authorized - Flickr will grant the access token to Facebook.

14.Now, Facebook can access Flickr to get photos on behalf of the user.

15.Let me summarize what each technology is for.

OAuth - a machine authorisation protocol - gives permission for a system to access your account.

OpenID - provides decentralized single sign on.

InfoCards - provides phishing resistant authentication.

Sunday, November 16, 2008

Security Policy with Rampart - UserNameToken

This post explains security policy basics with an example in Rampart.

Here the service expects clients to authenticate themselves with username/password credentials.

Usually non-functional descriptions and QoS aspects are covered by WS-Policy - and assertions related to security are covered under WS-SecurityPolicy.

You can read more about WS-SecurityPolicy from here.

Let's see how the above requirements being expressed in service policy.

<service name="SimpleService">
<module ref="rampart" />

<operation name="echo">
<messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>

<parameter name="ServiceClass" locked="false">org.apache.ws.axis2.SimpleService/>

<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:SupportingTokens
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:SupportingTokens>

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
<ramp:passwordCallbackClassvorg.apache.ws.axis2.PWCBHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</service>
Lets start with the most inner part.

<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"/>
</wsp:Policy>
The above requests to include username/password credentials in all the messages sent from initiator to recipient.

<sp:SupportingTokens
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
</sp:SupportingTokens>
Supporting tokens are included in the security header and in our case UsernameToken will be expected in the header.

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
<ramp:passwordCallbackClass>org.apache.ws.axis2.PWCBHandler</ramp:passwordCallbackClass>
</ramp:RampartConfig>
This is not policy specific - but Rampart specific - it simply says Rampart to where to find the password callback class at the service end to verify client credentials.

That's it with policy, you need to write the service class and the password callback handler for the service. This may help you in doing that.

Now, lets look at the client.

Use wsdl2java tool to generate service stubs.

wsdl2java -uri http://localhost:8080/axis2/services/SimpleService?wsdl -ns2p http://axis2.ws.apache.org=org.apache.ws.axis2 -uw

package org.apache.ws.axis2;

import java.rmi.RemoteException;

import org.apache.axis2.client.Options;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;

public class Client {

private static final String SERVICE_EPR = "http://localhost:8080/axis2/services/SimpleService";

public static void main(String[] args) throws RemoteException {
SimpleServiceStub service = null;
Options options = null;
ConfigurationContext configContext = null;

configContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem("repo", null);

service = new SimpleServiceStub(configContext, SERVICE_EPR);

// engage rampart module
service._getServiceClient().engageModule("rampart");

options = service._getServiceClient().getOptions();
// add username/password credentials
options.setUserName("bob");
options.setPassword("bobPW");

System.out.println(service.echo("Hi"));
}
}

Saturday, November 15, 2008

Axis2 client side module engagement

This post explains the different options available for engaging modules at the Axis2 client side.

Let's start with engaging modules with axis2.xml.

<axisconfig name="AxisJava2.0">
<!-- engaging rampart module -->
< module ref="rampart" />
</axisconfig>
With this, we have asked Axis2 to load the module rampart.

But, how come axis2 knows - from where to locate the above axis2.xml.

ConfigurationContext configContext = null;
configContext= ConfigurationContextFactory.createConfigurationContextFromFileSystem("path_to_repo","path_to_axis2_xml");
In this case axis2.xml will be loaded from path_to_axis2_xml and the corresponding modules will be loaded from path_to_repo/modules directory. So in our case you need to have the rampart module inside path_to_repo/modules directory.

Following is another way - how you can tell Axis2 to load your axis2.xml.

ConfigurationContext configContext = null;
configContext= ConfigurationContextFactory.createConfigurationContextFromFileSystem("path_to_repo",null);
Here we pass null for path_to_axis2_xml.

In this case Axis2 will try to find out axis2.xml from directly under path_to_repo location.

Notice here that the name of the configuration file must be axis2.xml.

Doing all these above is still not enough - we need to load ConfigurationContext we created into the service Stub.

SimpleServiceStub service = null;
service = new SimpleServiceStub(configContext, SERVICE_EPR);
All set and we are done with the first option.

BTW, there is another way you can ask Axis2 to load your axis2.xml without creating a ConfigurationContext and passing it to the service Stub. In this case you need to set following system properties.

import org.apache.axis2.Constants;

System.setProperty(Constants.AXIS2_CONF, "path_to_axis2_xml");
System.setProperty(Constants.AXIS2_REPO, "path_to_repo");
Even in this case, if you only set the AXIS2_REPO property then the axis2.xml will be read from directly under path_to_repo location.

With this option you need not to create and load the ConfigurationContext in to the service Stub.

SimpleServiceStub service = null;
//service = new SimpleServiceStub(configContext, SERVICE_EPR);
service = new SimpleServiceStub(SERVICE_EPR);
Let's move to the next option - that is engaging modules programmatically.

Following shows how to do that.

SimpleServiceStub service = null;
service = new SimpleServiceStub(SERVICE_EPR);
service._getServiceClient().engageModule("rampart");
Here we do not need to tell Axis2, where to find the axis2.xml - but still we need to tell where to locate the module - that is the path_to_repo.

We can do it by just setting the Constants.AXIS2_REPO system property as below.

System.setProperty(Constants.AXIS2_REPO, "path_to_repo");
OR we can create a ConfigurationContext with path_to_repo and pass it to the service stub.

SimpleServiceStub service = null;
ConfigurationContext configContext = null;

configContext= ConfigurationContextFactory.createConfigurationContextFromFileSystem("path_to_repo",null);

//service = new SimpleServiceStub(SERVICE_EPR);
service = new SimpleServiceStub(configContext, SERVICE_EPR);
That's it - and we are done with the second option as well.

War for PEACE not for a PIECE - Troops move into Pooneryn

LTTE lost their stronghold of Pooneryn in the latest battle with SLA.

It's just a matter of time to reach Kilinochchi - to end the massacres of blood loving Vellupillai Piribaharan.

Tigers do fight for a piece, a piece of land - Sri Lankan army fight for peace, to bring peace for all of us.

Lets wish them immense courage and determination.

Friday, November 14, 2008

WSO2 Synergies: ESB and Registry - Free webinar

This webinar, on 18th November 2008, will introduce you to both the WSO2 ESB and the WSO2 Registry, then focus on how these two technologies can collaborate to achieve goals such as centralized configuration and control of a mediation network, automatic run-time enforcement of design-time policies, business activity monitoring.

You should attend this webinar if you:

- Currently use, or are thinking about using, the WSO2 Registry or WSO2 ESB

- Are curious about the relationship between design-time and run-time governance

- Want to be able to centrally manage a network of ESB instances

Your presenter, Asanka Abeysinghe, is a WSO2 architect, who focuses on vertical market capabilities, including financial services. Asanka is also a committer of the Apache Software Foundation where he contributes to the Apache Synapse ESB project, upon which the WSO2 ESB is based.

Wildcard certificates with java keytool

Wildcard SSL Certificates let you secure an unlimited number of sub-domains under a single domain name.

This approach has many advantages as well as can be some pitfalls.

Being cheap, ability to secure multiple sub domains and easy to manage are some of the advantages.

At the same time, following lists few pitfalls.

Security: If one server or sub-domain is compromised, all sub-domains may be compromised.

Management: If the wildcard certificate needs to be revoked, all sub-domains will need a new certificate.

Compatibility: Wildcard certificates may not work seamlessly with older server-client configurations.

Let's see how we can create a wildcard certificate with java key tool.

Thursday, November 13, 2008

Dumping out JKS private key

This post explains how you could programmatically access a java key store and dump its private key out.

To start with, let's create a key store with java keytool.

C:\>keytool -genkey -keystore wso2.jks -storepass wso2123 -keypass wso2123 -alias wso2
Let's look at the code now. It's self-explanatory with the attached comments.

package org.wso2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyStore;

public class KeyExplorer {

private final static String KEY_STORE_FILE = "c:\\wso2.jks";
private final static String KEY_STORE_PASSWORD = "wso2123";
private final static String PRIVATE_KEY_PASSWORD = "wso2123";
private final static String KEY_ALIAS = "wso2";
private final static String KEY_STORE_TYPE = "jks";
private final static String OUT_PUT_FILE = "c:\\wso2.key";

public static void main(String[] args) {

KeyStore keystore = null;
Key privateKey = null;
FileOutputStream outFile = null;

try {
keystore = KeyStore.getInstance(KEY_STORE_TYPE);
keystore.load(new FileInputStream(KEY_STORE_FILE), KEY_STORE_PASSWORD.toCharArray());

if (keystore.containsAlias(KEY_ALIAS)) {
privateKey = keystore.getKey(KEY_ALIAS, PRIVATE_KEY_PASSWORD.toCharArray());
} else {
return;
}

// Returns true if the entry identified by the given alias was
// created by a call to setKeyEntry, or created by a call to
// setEntry with a PrivateKeyEntry or a SecretKeyEntry.
if (keystore.isKeyEntry(KEY_ALIAS)) {
System.out.println("PrivateKeyEntry");
}

// Returns the standard algorithm name for this key. For example,
// "DSA" would indicate that this key is a DSA key.
System.out.println("Algorithm: " + privateKey.getAlgorithm());

// Returns the name of the primary encoding format of this key, or
// null if this key does not support encoding. The primary encoding
// format is named in terms of the appropriate ASN.1 data format, if
// an ASN.1 specification for this key exists. For example, the name
// of the ASN.1 data format for public keys is SubjectPublicKeyInfo,
// as defined by the X.509 standard; in this case, the returned
// format is "X.509". Similarly, the name of the ASN.1 data format
// for private keys is PrivateKeyInfo, as defined by the PKCS #8
// standard; in this case, the returned format is "PKCS#8".
System.out.println("Key format: " + privateKey.getFormat());

outFile = new FileOutputStream(OUT_PUT_FILE);
outFile.write(privateKey.getEncoded());
outFile.flush();

} catch (Exception e) {
e.printStackTrace();
} finally {
if (outFile != null) {
try {
outFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
The above will output the private key in a binary format - let's see how we could convert it to Base64 with OpenSSL.

You can download OpenSSL for Windows from here.

openssl enc -in wso2.key -out wso2-txt.key -a
The "enc" command does various types of encryptions and encodings and "-a" is for Base64 - just type "openssl enc -help" - you'll see all available options.

Wednesday, November 12, 2008

Federated Identities with Information Cards

This post explains how you could easily build an Identity Federation to enable Cross Company Authentication[CCA] with InfoCards.

Federated Identity is a mechanism which allows authentication across different enterprises in different trust domains - based on a trust factor.

I'll be using WSO2 Identity Solution to demonstrate how this could be achieved.

WSO2 Identity Solution, can be used as an Identity Provider to issue InfoCards - at the same time it comes with a set of relying party[RP] components to facilitate InfoCard based logins.

Let me explain the use case, first.

Employees of the 'Company A' need access to services hosted in 'Company B' and 'Company C'.

Both companies willing to share service access with 'Company A' - but, employees from 'Company A' should authenticate first and should prove that they are from 'Company A'.

The straight forward solution is to share 'Company A's' user store with the others.

But, this is not an extensible solution - when employees join and leave from 'Company A'.

This is where the Information Cards based solution fits in with WSO2 Identity Solution.

In this demonstration, I assume that the user store in 'Company A' is based on Active Directory [AD].

So, to start with, let's deploy WSO2 Identity Solution over 'Company A's' AD.

All the steps you need to know there are explained here in my previous blog post.

Please follow the exact steps given there and I'll be using the same names introduced in that post.

Once done with it - the user 'prabath' from 'Company A', could login to the WSO2 Identity Solution and download an Information Card, against his AD credentials.

Lets move to the next step - that is to 'Company B'.

'Company B' can use the Information Cards relying party components ship with WSO2 Identity Solution to protect their web applications.

First - you need to secure your Tomcat application server.

Please follow the exact steps given here - I'll be using the same names here - which were introduced there.

You can download the sample application for the 'Company B' from here.

Once downloaded copy all the jars inside 'endorsed' folder to [CATALINA_HOME]\common\endorsed and copy the 'javarp' folder to [CATALINA_HOME]\webapps.

I have already added the certificate of the WSO2 Identity Solution[hosted on localhost] to wso2is.jks - you can find inside 'javarp' folder - so, the 'Company C' is now ready to accept Information Cards from 'Company A' users.

Still - there is one step missing.

'Company A' - also should trust 'Company B' [as well as 'Company C'] to issue Information Cards to those.

This is how you do this.

In the machine you run the WSO2 Identity Solution you need to add the certificate of the CA who signed the 'Company B's' certificate to the [JAVA_HOME]\jre\lib\security\cacerts keystore. [here the default password is 'changeit'].

In our case, we used a self-signed certificate for the 'Company B' - so it's the same as 'Company B's' certificate.

You can save the certificate through the browser - by hitting the url, https://webapp:8443/javarp and following the appropriates steps.

Or you can simply export it from the keystore.jks.
:\>keytool import -alias webapp -file webapp.cer -keystore [JAVA_HOME]\jre\lib\security\cacerts
Next step is to tell the WSO2 Identity Solution, that you trust 'Company B'.

Here the admin can set 'Company B' as a trusted relying party by uploading it's certificate.

Go to https://localhost:12443/admin - login with admin/admin - select 'Trusted Relying Parties' and add the certificate.

All set - now the user 'prabath' from 'Company A' can visit 'Company B', say https://webapp:8443/javarp and login with his Information Card issued by 'Company A'.

Tuesday, November 11, 2008

Securing Tomcat

This post explains how you can enable SSL on Tomcat.

Let's first create a self-signed certificate.

[CATALINA_HOME]>keytool -genkey -alias webapp -keyalg RSA -keystore keystore.jks
The above will create a keystore with the name keystore.jks in side [CATALINA_HOME].

Also, notice that here we used webapp as the CN - so, we need to edit the file C:\WINDOWS\system32\drivers\etc\hosts and add the following entry - so we can make our certificate trusted by the browser.

127.0.0.1 webapp

Now, lets edit - [CATALINA_HOME]\conf\server.xml to make Tomcat SSL enabled.

<Service name="Catalina">

<!-- Add the following -->
<!-- 'tomcat' is the password you gave while creating keystore.jks -->
<!-- Make sure keystore.jks is inside [CATALINA_HOME] -->
<Connector port="8443"
maxHttpHeaderSize="8192"
maxThreads="150"
minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false"
disableUploadTimeout="true"
acceptCount="100"
scheme="https"
secure="true"
clientAuth="false"
sslProtocol="TLS"
keystoreFile="keystore.jks"
keystorePass="tomcat"/>
</Service>
All set, hit the url, https://webapp:8443 - now broswer will give you a warning saying that your certificate is not trusted.

Install the cert in the browser to get rid of the warning.

UserNameToken authentication based on Active Directory

This post explains how you can authenticate users at the service end against an Active Directory[AD] - in the CallbackHandler.

This assumes that you know how to deploy a service and configure it to accept a UserNameToken for authentication.[If not please refer this.]

If you are curious to know more on CallbackHandlers - then this may help.

To get started, follow the first part in my previous post - that is to configiure AD with the user tomcat.

Now let's get started with password CallbackHandler at the service end.

package org.apache.ws.axis2;

import java.io.IOException;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

public class PWCBHandler implements CallbackHandler {

private static String SECURITY_PRINCIPAL = "cn=tomcat,cn=users,dc=home,dc=com";
private static String SECURITY_CREDENTIALS = "1qaz2wsx@";
private static String PROVIDER_URL = "ldap://192.168.1.2:389";
private static String USER_PATTERN = "cn={0},cn=Users,dc=home,dc=com";

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

ADAuthenticator authenticator = null;

authenticator = new ADAuthenticator(SECURITY_PRINCIPAL, SECURITY_CREDENTIALS, PROVIDER_URL,USER_PATTERN);

for (int i = 0; i < callbacks.length; i++) {

// When the server side need to authenticate the user
WSPasswordCallback pwcb = (WSPasswordCallback) callbacks[i];

// Usage value is set to USERNAME_TOKEN_UNKNOWN when the Rampart
// Engine wants the password callback handler to validate the
// username and password in the Username Token
if (pwcb.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {
if (authenticator.authenticate(pwcb.getIdentifer(), pwcb.getPassword())) {
return;
} else {
throw new UnsupportedCallbackException(callbacks[i], "check failed");
}
}
}
}
}
Now lets look at the ADAuthenticator - which actually does the authentication against the AD - user base.

package org.apache.ws.axis2;

import java.text.MessageFormat;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

public class ADAuthenticator {

/**
* Holds the name of the environment property for specifying the initial
* context factory to use. The value of the property should be the fully
* qualified class name of the factory class that will create an initial
* context.
*/
private static String INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";

private DirContext context = null;
private String userPattern = null;

/**
*
* @param conName
* Holds the name of the environment property for specifying the
* identity of the principal for authenticating the caller to the
* service.
* @param conPassword
* Holds the name of the environment property for specifying the
* credentials of the principal for authenticating the caller to
* the service.
* @param connectionUrl
* Holds the name of the environment property for specifying
* configuration information for the service provider to use.
* @param userPattern
* Search pattern.
*/
public ADAuthenticator(String conName, String conPassword, String connectionUrl,
String userPattern) {

Hashtable environment = null;

try {
environment = new Hashtable();
environment.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
environment.put(Context.SECURITY_PRINCIPAL, conName);
environment.put(Context.SECURITY_CREDENTIALS, conPassword);
environment.put(Context.PROVIDER_URL, connectionUrl);
this.userPattern = userPattern;

context = new InitialDirContext(environment);
} catch (NamingException ex) {
throw new RuntimeException();
}
}

/**
* Authenticates a given user against Active Directory user store.
*
* @param userName
* User to be authenticated.
* @param password
* Password of the user.
* @return true if authenticated.
*/
public boolean authenticate(String userName, String password) {

String dn = null;

try {
dn = MessageFormat.format(userPattern, new String[] { userName });
return this.bindAsUser(this.context, dn, (String) password);
} catch (NamingException ex) {
return false;
}

}

private boolean bindAsUser(DirContext context, String dn, String credentials)
throws NamingException {

if (credentials == null || dn == null)
return false;

context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);

context.getAttributes("", null);

return true;
}

}

Monday, November 10, 2008

Buliding your own CA infrastructure with Windows 2003

This post explains how you can build your own Certfication Authority[CA] with Windows 2003.

I have explained the same in my previous post - but with OpenSSL.

1. First make sure that you have installed Certificate Services.

2. Select stand-alone root CA.

3. Set private/public key pair for the CA.

4. Set certificate database settings.

5. Done. Your CA is ready to issue certificates.

Let's create private/public key pair for any server and generate a certificate signing request [CSR] for it.
[SAMPLE]\>keytool -genkey -alias wso2wsas -keyalg RSA -keystore wso2wsas.jks

[SAMPLE]\>keytool -certreq -keystore wso2wsas.jks -alias wso2wsas -file wso2wsas.cert.req
The above will generate the CSR - wso2wsas.cert.req, which we can submit to the CA which we just created, to get it signed.

1. Go to Control Panel --> Certification Authority.

2. Right click on WSO2CA --> All Tasks --> Submit New Request --> Submit the file wso2wsas.cert.req.

3. Now, you'll find the submitted request under, 'Pending Requests'.

4. Righ click on it --> All Tasks --> Issue.

You'll find the signed certificate under 'Issued Certificates'.

Sunday, November 9, 2008

Deploying WSO2 Identity Solution over Active Directory

This post explains all the steps you need to know in deploying WSO2 Identity Solution over Microsoft Active Directory [AD].

Lets get started with setting up the AD.

I have setup my AD on Windows 2003 Server [IP:192.168.1.3] and it looks like as shown in the image below.

Let's first create an AD user, which can be used by WSO2 IS to access AD.

This user can be of any name, we'll just say 'identity'

Now we need to delegate the task, 'Read all user information' - to the user 'identity'.

[Users --> Right Click --> Delegate Control]

Done, with it. Lets create another user called 'prabath' - where this user represents any user in the AD - who can connect to the WSO2 IS and download an Information Card against his corresponding AD profile.

All set - we are done with the AD configurations - let's setup Identity Solution.

Download the latest code from the SVN repo: https://svn.wso2.org/repos/wso2/trunk/solutions/identity

Then, from the root directory (say [Identity] ) of the downloaded code.

[Make sure you have installed Maven2]

:\> mvn -Drelease clean install

The above will create a zip file distribution at [Identity]\modules\distribution\target.

Unzip the Zip file to a local folder.

You also need to download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 5.0 from here and copy the two jar files from the extracted jce directory (local_policy.jar and US_export_policy.jar) to $JAVA_HOME/jre/lib/security.

Start WSO2 Identity Solution with [IS_INSTALLED_DIR]\bin\wso2is.bat.

Go to url : https://localhost:12443/admin and login with admin/admin [user/password] - then select 'User Stores and then 'Add new user store'.

Select LDAPRealm.

Set LDAPRealm properties. You can find the availabe AD attribute names from here.

Set Active_Directory realm as the default.

Click on 'Define Claims' and select 'Given name' and 'Email address' [Dont uncheck any claims which are already selected]

Click on 'Claim Mappings'.

Click on 'Given name' and 'Email address' and do the claim mapping appropriately.

All set, hit the url https://localhost:12443 and login with your AD user credentials.

Now you can download an Information Card from WSO2 IS, against your AD account.

Saturday, November 8, 2008

Secure your services with Active Directory user credentials

This post explains how you could secure your web srevices with Microsoft Active Directory[AD] user credentials.

In other words, your services could only be accessed by a set of users who belongs to a certain group in AD.

Let's start configuring AD first.

I have setup my AD on Windows 2003 Server [IP:192.168.1.3] and it looks like as shown in the image below.

In our case we'll be deploying our services with Axis2 running on Tomcat.

You can download Axis2 WAR distribution from here and deploy it on Tomcat.

In order to authenticate users against AD, Tomcat should have access to the AD user base.

Let's first create an AD user, which can be used by Tomcat to access AD.

This user can be of any name, we'll just say 'tomcat'.

Now we need to delegate the task, 'Read all user information' - to the user 'tomcat'.

[Users --> Right Click --> Delegate Control]

Done, with it. Lets create a new group called 'facilelogin' - where the members of this group will be given access to the secured web services.

Once created, add any user to that group - I'll add the 'administrator' - so, this user will be able to access secured services.

All set - we are done with the AD configurations - let's setup Tomcat.

Find the file [CATALINA_HOME]\conf\server.xml and edit to add the following.
<!-- You need to add new configurations inside this tag -->
<Engine name="Catalina" defaultHost="localhost">

<!-- Make sure you comment out this line from your current configuration -->
<!-- Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/ -->

<!-- Add following new configuration -->
<!-- Compare the values with the Active Directory image shown above -->  
<!-- I am running my AD on 192.168.1.3 -->    
<Realm className="org.apache.catalina.realm.JNDIRealm"
connectionName="cn=tomcat,cn=users,dc=home,dc=com"
connectionPassword="1qaz2wsx@"
connectionURL="ldap://192.168.1.3:389"
userRoleName="member"
userBase="cn=Users,dc=home,dc=com"
userPattern="cn={0},cn=Users,dc=home,dc=com"
roleBase="cn=Users,dc=home,dc=com"
roleName="cn"
roleSearch="(member={0})"
roleSubtress="false"
userSubtree="true"
/>

</Engine>
Once this is done - let's open the file [CATALINA_HOME]\webapps\axis2\WEB-INF\web.xml and edit to add the following.
<web-app> 

<security-constraint> 
<web-resource-collection>
<web-resource-name>secured services 
<url-pattern>/services/* 
</web-resource-collection>
<auth-constraint> 
<!-- This is mapped to the 'facilelogin' group in AD -->
<role-name>facilelogin 
</auth-constraint> 
</security-constraint> 

<login-config> 
<auth-method>BASIC 
<realm-name>facilelogin 
</login-config> 

</web-app>
All set - let's restart Tomcat.

Let's see how we could invoke the above secured service with a web service client.

Here, the only difference is you need to setup the HTTP header attributes appropriately.

Also, in this case user credentials are not carried by the SOAP header, but the transport header.
package org.apache.ws.axis2;

import java.util.Properties;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HttpTransportProperties;

public class Client {

public static void main(String[] args) throws Exception {
ServiceClient client = null;
Options options = null;
OMElement response = null;
HttpTransportProperties.Authenticator authenticator = null;
Properties properties = null;

client = new ServiceClient();
options = new Options();
options.setAction("urn:getVersion");
options.setTo(new EndpointReference("http://localhost:8080/axis2/services/Version"));

authenticator = new HttpTransportProperties.Authenticator();
// Add user creadentials to the transport header
// This user belongs to the 'facilelogin' group in AD
authenticator.setUsername("administrator");
authenticator.setPassword("prabath");
authenticator.setRealm("facilelogin");

properties = new Properties();
properties.put(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);

options.setProperties(properties); 
client.setOptions(options);

response = client.sendReceive(getPayload());

System.out.println(response);
}

private static OMElement getPayload() {
OMFactory factory = null;
OMNamespace ns = null;
OMElement elem = null;
factory = OMAbstractFactory.getOMFactory();
ns = factory.createOMNamespace("http://axisversion.sample", "ns1");
elem = factory.createOMElement("getVersion", ns); 
return elem;
}
}

Friday, November 7, 2008

Secure your services with HTTP Basic Authentication

This post explains how you could secure your web service with HTTP Basic Authentication.

Let's download Axis2 WAR distribution from here and deploy it on Tomcat.

Start Tomcat, and hit the url, http://localhost:8080/axis2/services/Version?wsdl, you'll find no issues in accessing it.

Let's try to protect Axis2 services with HTTP Basic Authentication.

We need to define a new role first, who will be given access.

Find the file [CATALINA_HOME]\conf\tomcat-users.xml and edit it to add the following role and the user.

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="facilelogin"/>
<user username="prabath" password="prabath" roles="facilelogin"/>
</tomcat-users>
Now let's restricts access to all our web services to the above defined role.

Open the file [CATALINA_HOME]\webapps\axis2\WEB-INF\web.xml and edit to add the following.

<web-app>

<security-constraint>
<web-resource-collection>
<web-resource-name>secured services</web-resource-name>
<url-pattern>/services/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>facilelogin</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>facilelogin</realm-name>
</login-config>

</web-app>
All set - let's restart Tomcat.

Hit the url, http://localhost:8080/axis2/services/Version?wsdl and you'll be asked for the user credentials.

Let's see how we could invoke the above secured service with a web service client.

Here, the only difference is you need to setup the HTTP header attributes appropriately.

Also, in this case user credentials are not carried by the SOAP header, but the transport header.

package org.apache.ws.axis2;

import java.util.Properties;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HttpTransportProperties;

public class Client {

public static void main(String[] args) throws Exception {
ServiceClient client = null;
Options options = null;
OMElement response = null;
HttpTransportProperties.Authenticator authenticator = null;
Properties properties = null;

client = new ServiceClient();
options = new Options();
options.setAction("urn:getVersion");
options.setTo(new EndpointReference("http://localhost:8080/axis2/services/Version"));

authenticator = new HttpTransportProperties.Authenticator();
// Add user creadentials to the transport header
authenticator.setUsername("prabath");
authenticator.setPassword("prabath");
authenticator.setRealm("facilelogin");

properties = new Properties();
properties.put(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);

options.setProperties(properties);
client.setOptions(options);

response = client.sendReceive(getPayload());

System.out.println(response);
}

private static OMElement getPayload() {
OMFactory factory = null;
OMNamespace ns = null;
OMElement elem = null;
factory = OMAbstractFactory.getOMFactory();
ns = factory.createOMNamespace("http://axisversion.sample", "ns1");
elem = factory.createOMElement("getVersion", ns);
return elem;
}
}


POST /axis2/services/Version HTTP/1.1
Content-Type: text/xml; charset=UTF-8
SOAPAction: "urn:getVersion"
User-Agent: Axis2
Transfer-Encoding: chunked
Authorization: Basic cHJhYmF0aDpwcmFiYXRo
Host: localhost:8080

Thursday, November 6, 2008

The many meanings of Open Source

This post summarizes the research paper 'The many meanings of Open Source' by Cristina Gacek, Tony Lawrie, and Budi Arief.

Software can be categorized as Free and Proprietary Software.

Proprietary software does not make its source code available to the public and redistribution and modification is prohibited.

Shareware and Freeware are part of the proprietary software. Both forms are available as free downloads, but shareware is for limited usage. To go beyond the limitations, shareware expects a license fee to be paid.

Free Software can be further divided in to two as Non-copylefted and Copylefted software.

Copylefted software does not permit additional restrictions to be enforced. Though it’s open for modifications and redistribution, it needs to be free, always.

The most prominent distribution terms for copylefted software are covered in the GNU GPL (General Public License).

Non-copylefted is the opposite. It is allowed to modify and redistribute as proprietary software.

The idea of building software within a cooperating community, where the source code was made available so that everyone could modify and redistribute it began with the GNU project at MIT in the early 1980s.

In 1985 the Free Software Foundation (FSF) was pioneered by Richard Stallman to generate some income for the free software movement, not restricting itself to GNU.

Free software, as defined by the FSF, is a program that grants various freedoms to its users.

-Freedom to run the program for any purpose

-Freedom to study and adapt the code for personal use

-Freedom to redistribute copies of the program, either gratis or for a fee

-Freedom to distribute improved or modified versions of the program


In the early 1998, the term Open Source was coined as a response to the announcement made by Netscape on its plan to give away the source code of its web browser.

Immediately afterwards, the Open Source Initiative (OSI) was set up to manage and promote the Open Source Definition (OSD).

To qualify as an open source project, the basic requirement is to follow the OSD [Open Source Definition].

OSD emphasizes nine criteria that open source software needs to be complying with. The main three are:

1. The ability to distribute the software freely.

2. The availability of the source code.

3. The right to create derived works through modification.


The rest of the criteria deals with the licensing issues.

4. The integrity of the author’s source code must be preserved, making the source of changes clear to the community

5. No discrimination against persons or groups both for providing contributions and for using the software

6. No restriction on the purpose of usage of the software, providing no discrimination against fields of endeavor

7. The rights attached to the software apply to all recipients of its (re)distribution

8. The license must not be specific to a product, but apply to all sub-parts within the licensed product

9. The license must not contaminate other software, permitting the distribution of other non-open source software along with open source one


Open source software differs from Free Software.

They disagree on basic principles such as commercialism, licensing and etc, but not on practical recommendations – availability of source code, ability to modify the code and etc.

Characteristics of open source can be mainly divided in to two, Common and Variable.

Common characteristics include Community, Development improvement cycles, Motivation, Developers and users, Process of accepting submissions and Modularity.

Variable characteristics include Choice of work area, Size, Operational support, Licensing, Visibility of software architecture, Submission dissemination information process, Meritocratic culture, Documentation and Testing, Business model, Starting points, Decision making support and Balance of centralization and decentralization.

Open source users can be categorized into two, Passive users and Active users.

Active users are further divided as Non-developers and Developers.

Non-developers do report bugs and suggest new features.

Developers are further divided, Co-developers and Core developers.

Co-developers do report bugs, suggest new features and review & modify the code.

Core developers defer from Co-developers since they involve in decision making.

Tuesday, November 4, 2008

Managing deployed Axis2 services

There can be certain tasks we need to carry out,

1. at the time a service/service group being added
2. at the time a service/service group being updated
3. at the time a service/service group being deleted.

Let's say we need to have a service parameter for a given service.

But, there is no way we could derive the value of it at the time of development and that value is specific to the deployment setup.

Say you want to pass the key store name and it's key name as service parameters.

These value differ from one deployment setup to another.

So, setting up that parameter in the services.xml will not work.

But, we can setup it at the time service being deployed.

To do this, we need to register an event listener in axis2.xml.

The event listener is an implementation of org.apache.axis2.engine.AxisObserver.

Let's see a sample implementation of AxisObserver and how we could configure it in axis2.xml.

import java.util.ArrayList;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.AxisServiceGroup;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.engine.AxisEvent;
import org.apache.axis2.engine.AxisObserver;

public class ServiceInterceptor implements AxisObserver {

public void init(AxisConfiguration arg0) {
}

public void moduleUpdate(AxisEvent arg0, AxisModule arg1) {
}

public void serviceGroupUpdate(AxisEvent arg0, AxisServiceGroup arg1) {
}

public void serviceUpdate(AxisEvent event, AxisService service) {
int eventType;
String serviceName = null;

eventType = event.getEventType();
serviceName = service.getName();

if (eventType == AxisEvent.SERVICE_DEPLOY) {
} else if (eventType == AxisEvent.SERVICE_START) {
if ("MyService".equals(serviceName)) {
}
} else if (eventType == AxisEvent.SERVICE_STOP) {
} else if (eventType == AxisEvent.SERVICE_REMOVE) {
}
}

public void addParameter(Parameter arg0) throws AxisFault {
}

public void deserializeParameters(OMElement arg0) throws AxisFault {
}

public Parameter getParameter(String arg0) {
return null;
}

public ArrayList getParameters() {
return null;
}

public boolean isParameterLocked(String arg0) {
return false;
}

public void removeParameter(Parameter arg0) throws AxisFault {
}

}
Now, we need to configure the listener in [AXIS2_HOME]\conf\axis2.xml.

<listener class="ServiceInterceptor"/>

Debugging with Simple Axis2 Server

You can launch Axis2 with [AXIS2_HOME]\bin\axis2server.bat.

To enable remote debugging, edit axis2server.bat, find the following line,

"%_JAVACMD%" %JAVA_OPTS% -cp "!AXIS2_CLASS_PATH!"
org.apache.axis2.transport.SimpleAxis2Server %AXIS2_CMD_LINE_ARGS%
And, replace it with the following.

"%_JAVACMD%" %JAVA_OPTS% -cp "!AXIS2_CLASS_PATH!"-Xdebug -Xnoagent
-Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
org.apache.axis2.transport.SimpleAxis2Server %AXIS2_CMD_LINE_ARGS%

Monday, November 3, 2008

Secure Conversation with WCF

This post takes you through basics of Secure Conversation concepts through an example written in WCF.

You can download the sample solution[VS 2008] from here.

The solution contains two projects.

- StockQuoteService [The WCF service]
- StockQuoteClient

Set StockQuoteService as the 'Startup Project' and then press Ctrl+F5 to run the service.

Then run the StockQuoteClient by setting it as the 'Startup Project' and pressing Ctrl+F5.

Let's look at service and client configurations and you'll find that we use basicHttpBinding.
<!--[StockQuoteService]\app.config -->
<service name="wso2.org.stockquotes.StockQuoteService" behaviorConfiguration="stockquotebehaviour">
<endpoint address="http://localhost/stocks"
binding="basicHttpBinding"
contract="wso2.org.stockquotes.StockQuoteService" />
<endpoint contract="IMetadataExchange"
binding="mexHttpBinding"
address="mex" />
</service>
<!--[StockQuoteClient]\app.config -->
<client>
<endpoint address="http://localhost/stocks"
binding="basicHttpBinding" 
contract="StockQuoteServiceProxy.StockQuoteService" />
</client>
Now to enable logging, add following to the [StockQuoteService]\app.config.
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" 
switchValue="Warning, ActivityTracing">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelMessageLoggingListener">
<filter type="" />
</add>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="c:\messages.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
</sharedListeners>
</system.diagnostics>

<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMessagesAtTransportLevel="true" />
</diagnostics>
</system.serviceModel>
Now lets start both the service and the client.

You can view the log file with SvcTraceViewer tool comes with .NET - launch the tool and simply open the file c:\messages.svclog. You'll find everything in clear text.

Let's change the binding from basicHttpBinding to wsHttpBinding in both the client and service configuration files, delete the file c:\messages.svclog and run both the service and the client.

WCF turns on Secure Conversation by default for all bindings that support WS-Security (WsHttpBinding, NetTcpBinding, netMsmqBinding).

Once again let's open the c:\messages.svclog file in SvcTraceViewer.

Now you'll see 8 messages instead of one.

Let's go through each and every one of them to understand Secure Conversation.

There are different ways to create a Security Context Token[SCT] to establish a Secure Conversation.

One way is through a Security Token Service[STS], which I discussed in this post.

The other two ways are,

1. The SCT is created by one of the communicating parties and propagated with the message.
2. The SCT created through negotiation/exchanges.

The example which we just discussed occupies the second option, that is the SCT created through negotiation/exchanges.

For this scenario the initiating party first sends a RquestSecurityToken[RST]. That is in our case from the client to the service.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:d55bd2a7-bae8-4751-a010-07e95fd82ee2</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost/stocks</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAABAAAAt7IY4gQABAA0AAAADAAMACgAAAAFAs4OAAAAD1BSQUJBVEgtSE9NRUhPTUU=
</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
Now the service will create a RequestSecurityTokenResponse[RSTR] and will send it back to the client.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
<a:RelatesTo>urn:uuid:d55bd2a7-bae8-4751-a010-07e95fd82ee2</a:RelatesTo>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAACAAAACAAIADgAAAA1wpnipKbRWTHfQGhYyuMAAAAAAIIAggBAAAAABQLODgAAAA9
IAE8ATQBFAAIACABIAE8ATQBFAAEAGABQAFIAQQBCAEEAVABIAC0ASABPAE0ARQAEABAAaABvAG0
AZQAuAGMAbwBtAAMAKgBwAHIAYQBiAGEAdABoAC0AaABvAG0AZQAuAGgAbwBtAGUALgBjAG8
AbQAFABAAaABvAG0AZQAuAGMAbwBtAAAAAAA=
</t:BinaryExchange>
</t:RequestSecurityTokenResponse>
</s:Body>
</s:Envelope>
To complete the initial handshaking process client once again sends an RSTR.
<s:Envelope xmlns:s="..." xmlns:a="">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
<a:MessageID>urn:uuid:da46b6f3-b168-4aae-95fb-b9412cecf177</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost/stocks</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAADAAAAAAAAAEgAAAAAAAAASAAAAAAAAABIAAAAAAAA
</t:BinaryExchange>
</t:RequestSecurityTokenResponse>
</s:Body>
</s:Envelope>

Now the service will reply with the RequestedSecurityToken.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue</a:Action>
<a:RelatesTo>urn:uuid:da46b6f3-b168-4aae-95fb-b9412cecf177</a:RelatesTo>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponseCollection xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestedSecurityToken>
<c:SecurityContextToken u:Id="uuid-06c81281-3c66-4122-b7ef-07744a68756e-1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<c:Identifier>
urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858
</c:Identifier>
</c:SecurityContextToken>
</t:RequestedSecurityToken>
<t:RequestedAttachedReference>
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1">
</o:Reference>
</o:SecurityTokenReference>
</t:RequestedAttachedReference>
<t:RequestedUnattachedReference>
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:Reference>
</o:SecurityTokenReference>
</t:RequestedUnattachedReference>
<t:RequestedProofToken>
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://schemas.xmlsoap.org/2005/02/trust/spnego#GSS_Wrap">
</e:EncryptionMethod>
<e:CipherData>
<e:CipherValue>
AQAAAMaisligvVhlAAAAAINLoPtv3Zq34T52WXoXBI1YaL+V8cNjc+BxdMVKu
</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</t:RequestedProofToken>
<t:Lifetime>
<u:Created>2008-11-03T10:11:04.437Z</u:Created>
<u:Expires>2008-11-03T10:26:04.437Z</u:Expires>
</t:Lifetime>
<t:KeySize>256</t:KeySize>
</t:RequestSecurityTokenResponse>
<t:RequestSecurityTokenResponse 
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:Authenticator>
<t:CombinedHash>Zc9/3FeaG8b6/cs7jeTsHpZC8JlPjCgeun5I/4RRbd0=</t:CombinedHash>
</t:Authenticator>
</t:RequestSecurityTokenResponse>
</t:RequestSecurityTokenResponseCollection>
</s:Body>
</s:Envelope>

Now the client sends following message to the service.
<s:Envelope xmlns:s="..." xmlns:a="..." xmlns:u="...">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_4">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</a:Action>
<a:MessageID u:Id="_5">urn:uuid:b534d3de-1bcc-42fb-81b6-e472c797281f</a:MessageID>
<a:ReplyTo u:Id="_6">
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1" u:Id="_7">http://localhost/stocks</a:To>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-f422503c-7974-42ab-9b8a-c330727290e9-5">
<u:Created>2008-11-03T10:11:04.453Z</u:Created>
<u:Expires>2008-11-03T10:16:04.453Z</u:Expires>
</u:Timestamp>
<c:SecurityContextToken u:Id="uuid-06c81281-3c66-4122-b7ef-07744a68756e-1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<c:Identifier>urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858</c:Identifier>
</c:SecurityContextToken>
<c:DerivedKeyToken u:Id="_0" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1"></o:Reference>
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<c:DerivedKeyToken u:Id="_1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1">
</o:SecurityTokenReference>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3">
<e:DataReference URI="#_8">
</e:ReferenceList>
<e:EncryptedData Id="_8" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference URI="#_1">
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>.....
</e:CipherData>
</e:EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_2">
<e:EncryptedData Id="_3" Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>
Finally the service will repond with the following.
<s:Envelope xmlns:s="..." xmlns:a="..." xmlns:u="...">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_4">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT
<a:RelatesTo u:Id="_5">urn:uuid:b534d3de-1bcc-42fb-81b6-e472c797281f</a:RelatesTo>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-06c81281-3c66-4122-b7ef-07744a68756e-6">
<u:Created>2008-11-03T10:11:04.468Z</u:Created>
<u:Expires>2008-11-03T10:16:04.468Z</u:Expires>
</u:Timestamp>
<c:DerivedKeyToken u:Id="_0" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>
<!-- Removed--<
</c:Nonce>
</c:DerivedKeyToken>
<c:DerivedKeyToken u:Id="_1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3"></e:DataReference>
<e:DataReference URI="#_6"></e:DataReference>
</e:ReferenceList>
<e:EncryptedData Id="_6" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_2">
<e:EncryptedData Id="_3" Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>
Now the client will invoke the bussiness logic.

Sunday, November 2, 2008

Tweaking SAVAN : Persistent subscriptions

WS-Eventing defines a protocol for one Web service (called a "subscriber") to register interest (called a "subscription") with another Web service (called an "event source") in receiving messages about events (called "notifications" or "event messages").

The subscriber may manage the subscription by interacting with a Web service (called the "subscription manager") designated by the event source.

Savan is the WS-Eventing implementation for Apache Axis2.

This post is a continuation of my previous post, which demonstrates Savan with an example.

Before going any further with this, please make sure you have gone through it and have the example running.

The sample application included three components.

1. Event source[axis2-savan-event-source] - a stock quote service publishes stock prices iteratively

2. Event sink[axis2-savan-event-sink] - a stock quote consumer subscribes to the stock quote service to receive events

3. [axis2-savan-client] - initiates subscription for the stock quote consumer

By default Savan manages event subscriptions with an in-memory subscriber store.

That is, in case the server running the event source, got restarted, event sink no more will receive any events.

This post takes you through, explaining how to add a custom subscriber store to manage subscriptions in a persistence manner.

Let's get started with the custom subscriber store code.

package org.apache.ws.axis2;

import java.util.Iterator;

import org.apache.axis2.context.ConfigurationContext;
import org.apache.savan.SavanException;
import org.apache.savan.storage.SubscriberStore;
import org.apache.savan.subscribers.Subscriber;
import org.apache.savan.subscribers.SubscriberGroup;

public class CustomSubscriberStore implements SubscriberStore {

public void init(ConfigurationContext arg0) throws SavanException {
// TODO Initialize the storage
}

public void addSubscriberGroup(String arg0) throws SavanException {
// TODO Add subscriber to a subscriber group
}

public void addSubscriberToGroup(String arg0, Subscriber arg1) throws SavanException {
// TODO Add subscriber to a subscriber group
}

public void delete(String arg0) throws SavanException {
// TODO Delete a previously stored subscriber
}

public SubscriberGroup getSubscriberGroup(String arg0) throws SavanException {
// TODO Retrieve a previously stored subscriber group
}

public Subscriber retrieve(String arg0) throws SavanException {
// TODO Retrieve a previously stored subscriber
}

public Iterator retrieveAllSubscriberGroups() throws SavanException {
// TODO Retrieve a previously stored subscriber groups
}

public Iterator retrieveAllSubscribers() throws SavanException {
// TODO Retrieve previously stored subscribers
}

public void store(Subscriber arg0) throws SavanException {
// TODO Store the subscriber in the persistent data store
}

}
Once completed, add the jar containing the above class to [AXIS2_HOME]\lib.

Now we need to make sure, Savan module loads the above class.

That can be configured in savan-config.xml.

You can find savan-config.xml in side [AXIS2_HOME]\repository\modules\savan-SNAPSHOT.mar. [You may extract savan-SNAPSHOT.mar, do the changes to the configuration file and then zip it again as savan-SNAPSHOT.mar]

<subscriberStores>
<subscriberStore>
<key>default</key>
<class>org.apache.savan.storage.DefaultSubscriberStore</class>
</subscriberStore>
<!-- this is your new subscriber store, with the key 'custom' -->
<subscriberStore>
<key>custom
<class>org.apache.ws.axis2.CustomSubscriberStore
</subscriberStore>
</subscriberStores>
Now, we are almost done. Let's configure the event source[axis2-savan-event-source] to use our custom subscriber store.

You need edit services.xml of StockQuoteService and add a new parameter.

<service name="StockQuoteService" scope="application">

<module ref="savan" />
<module ref="addressing" />

<parameter name="ServiceClass" locked="false">org.apache.ws.axis2.StockQuoteService</parameter>
<!-- Set SubscriberStoreKey to the key you set for you custom subscriber store in savan-config.xml -->
<parameter name="SubscriberStoreKey" locked="false">custom</parameter>

</service>
All set - redeploy the event source service and test with the client.