Thursday, January 27, 2011

Being the way I am --- Je suis qui je suis.... :)

An elderly Chinese woman had two
large pots, each hung on the ends of a pole which she carried
across her neck...

One of the pots had a crack in
it while the other pot was perfect and always delivered
a full portion of water.

At the end of the long walks from
the stream to the house, the cracked pot arrived only half
full..

For a full two years this went
on daily, with the woman bringing home only one and a half
pots of water..

Of course, the perfect pot was
proud of its accomplishments.

But the poor cracked pot was ashamed
of its own imperfection, and miserable that it could only
do ha lf of what it had been made to do

After two years of what it perceived
to be bitter failure, it spoke to the woman one day by the
stream.

'I am ashamed of myself, because
this crack in my side causes water to leak out all the way
back to your house.'

The old woman smiled, 'Did you
notice that there are flowers on your side of the path,
but not on the other pot's side?'

'That's because I have always
known about your flaw, so I planted flower seeds on your
side of the
path, and every day while we walk
back, you water them.'

For two years I have been able
to pick these beautiful flowers to decorate the table.

Without you being just the way
you are, there would not be this beauty to
grace the house.'

Each of us has our own unique
flaw. But it's the cracks and flaws we each have that make
our lives together so very interesting and rewarding..

You've just got to take each person
for what they are and look for the good in them.

I just received the above story through an e-mail, and I just like it a lot and the moral it carries.... So I thought of sharing it in my blog too... Well.. I do not know who the original author of the above story is and if the story is being copyrighted... Being very thankful to the author, I would like to add if there is any copyright on the above story, please let me know so that I will remove it...

On a side note, I would like to mention about the topic I selected for this post... When I read  the story, it was the topic came to my mind... It is the motto of a person who also had added lot of good morals to our lives...

Saturday, January 22, 2011

Tenant management in WSO2 Carbon with a multi-tenanted embedded LDAP

You may refer to my previous blog post to get an idea what I meant by a multi-tenanted LDAP.

WSO2 stratos currently does tenant management with JDBC user store. And there is a requirement of achieving that functionality with a multi-tenanted LDAP too.

In achieving that requirement, the current solution that is there in WSO2 carbon takes a hybrid approach, where meta data related to tenants is stored in JDBC database and have the user stores of tenants in a LDAP server--which is an embedded Apache Directory Server.

Following is a high level design (as per now) related with tenant management with LDAP.


Here, apacheds-server component and apacheds orbit bundle are responsible in registering LDAPTenantManagementService (please refer the above diagram) and managing partitions in the embedded-ldap.

You can easily explore this functionality by installing multi-tenancy support on a WSO2 carbon based stand alone product. You may refer to post: Installing multi-tenancy support for WSO2 Identity Server.

In addition to the steps mentioned there, please take the following steps to connect a multi-tenanted IS to embedded ApacheDS LDAP and explore what happens in tenant management at the LDAP server's side:

Step 1: Place a file named: 'tenant-config.xml' in IS_HOME/repostory/conf directory with the following entry:


By default, it is the JDBC tenant manager that is being used and with this configuration file you can configure it to specify which tenant manager to be initialized at the initialization of the user manager.

Step 2: Start the server --> log in as admin and create few tenants as I  have described in the aforementioned post.

Step 3: Connect to the embedded-ApacheDS LDAP server through a LDAP browser like ApacheDS Studio. For that you need to:
      i. locate  user-mgt.xml file in IS_HOME/repository/conf 
     ii. obtain connection name and password to connect to the LDAP server from the browser. 

Step 4: View how separate partitions have been created for separate tenants as shown in the following image.


Few remarks:
      i. You can see the dc=cse,dc=com partition in the left panel of the above image and that is created for a tenant with the domain name: cse.com.
     ii. Inside that partition, two contexts have been created as ou=users and ou=groups to store the tenant user entries and group information.
     iii. At the creation of the partition, an entry of the tenant admin will also be created under the ou=users context. And that tenant admin can login to the system and create users for his tenant which will be stored in the particular tenant partition.

So above is one way you can manage users in LDAP in a multi-tenanted environment. But it is specific to ApacheDS and also to embedded-ApacheDS since we use ApacheDS API to manipulate storage.
We also need a solution where we can use any directory server that implements LDAP protocol to maintain the multi-tenanted user store. I will discuss about this in a future post.

Note of thanks...

I would like to make this post dedicated to thank who caused this blog a reality and motivated me for blogging.

First, it is from Prasad Sir (Prasad Samarakoon) that I got the idea of blogging. He shared the link of his blog which has a very nice collection of genuine thoughts. After visiting his blog and reading his posts, I was so inspired to start a blog of mine too. Thank you very much Sir for directing me towards blogging.

Though I started the blog with that inspiration, I could blog rarely because blogging somehow got to the last items of my to do list.

Then after completing my degree course, I came to work in WSO2 where blogging is highly encouraged and I was lucky enough to work under a leader who has a blog which is of course a great source of knowledge and  who motivates others to blog too. Thank you very much Prabath aiya (Prabath Siriwardana) for motivating me to write blogs.

Now I have given more priority to write blogs than earlier, specially on things what I learn new, hoping they would be helpful to someone, one day...

Multi-tenant aware LDAP

This is my short note on the usage of  a LDAP storage in a multi-tenant environment which I intend to refer in a future post.

Well.. what is LDAP?
It is simply a protocol that basically defines how entries are stored and retrieved in a directory like storage (or directory server). If you want to learn more about LDAP, please find a comprehensive tutorial here. There are several implementations of this protocol out there. OpenLDAP and ApacheDS are two such popular LDAP servers.

How tenant management related with LDAP?
As I mentioned before, directory servers implemented according to LDAP, can be used as efficient and secure storages to maintain organizational user information. For an example, users will be authenticated into various applications against the user credentials stored there.

Usually LDAP serves as organizational user store. Storage is created and maintained as a tree structure and different contexts become branches of the tree. Generally when it is used as organizational user store, user entries are stored under one such context, where we call it a flat LDAP storage.

In cloud computing, we have this concept of multi-tenancy where the same application/service (customized for each organization/tenant) is used by  users from different organizations independent from one another. In this case, it would be good if we can store users of different tenants in separate directory trees or in different contexts of the same tree, rather than storing all user entries in a flat LDAP storage. 

What is a multi-tenant aware LDAP?
So..
  • when we create a tenant in a cloud environment, if a separate directory structure or separate context is created in LDAP server for storing the users belonging to that particular tenant, 
  • when we search a user of a particular tenant, if the search can only be directed to the relevant partition or context in the LDAP server,
  • and if an admin of a particular tenant can connect to the LDAP server used in a multi-tenant application and view only the users of his tenant,
we can say it is a multi-tenant aware LDAP. In order to achieve the above requirements, we have to exploit the concepts of directory structure and access control mechanisms in LDAP.

Sunday, January 2, 2011

Identity Provisioning with Google Provisioning API

Let's write a simple OSGi bundle which implements the functionality of identity provisioning with Google Provisioning API.

Google provides several user  provisioning mechanisms as described here to allow organizational users into Google Apps domain. Among them, Google Provisioning API is a progammetical solution which gives more flexibility over managing user accounts in Google Apps domain.

Among the many functionalities it provides, I will demonstrate in this post how to sync the identity (password, first name and last name) of users in an organizational legacy user store such as LDAP user store,  with that of the google app domain so that users are provisioned with a unified identity across domains.

Following diagram gives an high level overview of what we are going to achieve:

Now let's start coding..
Step 1: Install Google Data API in your machine. You can follow the instructions on installing required dependencies and the installing API itself in the given links.

Step 2: Create a new java project as a maven module since we are going to build it using maven later. Let's first add the required jars to the project's class path and later add maven dependencies. Hence add the following two jars to the classpath:
       1. gdata/java/deps/google.commons.collect
       2. gdata/java/libs/gdata.appsforyourdomain

Step 3: Write a client class for the UserService of Google Apps for Your Domain GData API. There is a sample class called 'AppsForYourDomainClient.java' in gdata/java/sample/appsforyourdomain which includes functions for utilizing all the functionalities provided with Google Apps for Your Domain service.
Since here we focus only on identity provisioning, you will need only four  methods as below in your client class: (Only the first method is different from the sample, hence I only include the signatures of other three methods so that you can extract them from the above mentioned sample class)
public AppsForYourDomainClient(String adminEmail, String adminPassword,
      String domain) throws Exception {
    this(domain);
    userService = new UserService("gdata-sample-AppsForYourDomain-UserService");
    userService.setUserCredentials(adminEmail, adminPassword);
  }

protected AppsForYourDomainClient(String domain);

public UserEntry retrieveUser(String username)
      throws AppsForYourDomainException, ServiceException, IOException;

public UserEntry updateUser(String username, UserEntry userEntry)
      throws AppsForYourDomainException, ServiceException, IOException;

Step 4: Write another class with the methods as follows to create an object of the above client and utilize its methods to get the synchronization of identity (password, fname, lname) done.
private void createAppDomainClient(String adminEmail, String adminPassword, String domain) throws GoogleAppUserSyncException {

        try{
            private AppsForYourDomainClientgoogleAppsDomainClient=
                    new AppsForYourDomainClient(adminEmail,adminPassword,domain);

        }   catch(Exception e){
            String errorMsg="AppsForYourDomainClient could not be created.";
            throw new GoogleAppUserSyncException(errorMsg,e);
        }
    }

public void syncCredentials(String userName, String userPassword)
            throws GoogleAppUserSyncException {

        try {
            UserEntry user = googleAppsDomainClient.retrieveUser(userName);
            user.getLogin().setPassword(userPassword);
            UserEntry newUser = googleAppsDomainClient.updateUser(userName, user);
        } catch (Exception e) {
            String errorMsg = "Credentials could not be updated with the user:" 
                              + userName;
            throw new GoogleAppUserSyncException(errorMsg, e);

        }

public void syncName(String userName, String firstName, String lastName) 
            throws Exception {

        try {
            UserEntry user = googleAppsDomainClient.retrieveUser(userName);
            if (lastName != null) {
                user.getName().setFamilyName(lastName);
            }
            if (firstName != null) {
                user.getName().setGivenName(firstName);
            }
            googleAppsDomainClient.updateUser(userName, user);

        } catch (Exception e) {
            String errorMsg = "Name of the user could not be sync'ed with 
                               google account."
            throw new GoogleAppUserSyncException(errorMsg, e);
        }
    } 

Step 5: Intermediate Testing: Now we need to test whether our code works. For that, we need to have a Google Apps Domain with admin credentials. If you do not have one, you can create a 14 days trial account which gives all the features of a premium account from here.

After creating an account or using an existing account, you can give the required details (i.e admin email, password and domain) to create the client and test whether identity provisioning of a sample user account works.

Step 6: Make it a maven module. If you want to build it using the standard building tool maven, you have to specify the dependencies (that we added to class path in step2) in the pom.xml.

Google has not hosted maven dependencies of gdata API for some reason. I could find couple of 3rd party sites which has hosted maven dependencies of gdata API among which http://code.google.com/p/mandubian-mvn/wiki/AvailableVersions seemed to be a reliable one.
Its repository url is: http://mandubian-mvn.googlecode.com/svn/trunk/mandubian-mvn/repository/com/google/gdata/
Following is the repository and dependencies elements of pom.xml that I wrote:

        
            mandubian-mvn
            http://mandubian-mvn.googlecode.com/svn/trunk/mandubian-mvn/repository
        
        
            maven2-repository.dev.java.net
            http://download.java.net/maven/2/
        
    

    
        
            com.google.gdata
            gdata-appsforyourdomain-1.0
            1.41.3
        
        
            com.google.gdata
            gdata-client-1.0
            1.41.3
        
        
            com.google.gdata
            gdata-media-1.0
            1.41.3
        
        
            com.google.gdata
            gdata-core-1.0
            1.41.3
        
        
            javax.mail
            mail
            1.4.1
        
    
Step 7: As we mentioned at the beginning, our final goal was to make this an OSGi bundle.
For that, first you need to mention packaging as a bundle. Add the following element to pom.xml
bundle
And then use the maven-bundle plugin to make it  a OSGi bundle at the build time. Add the following element too to your pom.xml

        
            
                org.apache.felix
                maven-bundle-plugin
                1.4.0
                true
                
                    
                        ${pom.artifactId}
                        
                        ${pom.artifactId}
                        
                            /*Include packages of your code to be exported.*/
                        
                        
                            gdata-appsforyourdomain-1.0|gdata-client-1.0|
                            gdata-media-1.0|gdata-core-1.0|mail|tools
                            ;scope=compile|runtime;inline=false
                        
                        
                           com.google.common.collect,!com.sun.*,
                           !javax.activation;version="1.1",
                           !javax.crypto,javax.crypto.spec,!javax.net,javax.net.ssl,
                           !javax.security.auth.callback,!javax.security.sasl,
                        
                      
                  
             
        
    
Usually, when an OSGi bundle depends on any other modules, they should also be osgi-fied in order to be used in an OSGi environment. Therefore standard solution is to wrap them as orbit bundle. But in above I have used a work around--that is to have them as embedded dependencies in the OSGi bundle. Disadvantage of this approach is that it tries to import all the dependencies of those have been mentioned in embedded dependencies. So we have to explicitly mention inside import package tag, those should not be attempted to be imported.

Step 8:  Please pay attention to the license under which this gdata API is distributed, which is Apache License, if you are going to distribute your code developed using the gdata API.

Thanks for your patience reading..!! :)