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..!! :)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.