May 8, 2010

Creating an app for Google Apps Marketplace with Kay - part 1 (revised)

This entiry is obsoleted because Appengine SDK-1.3.4 was released, and it has a capability for OpenID authentication. I'll definitely start figuring an easy way for writing Google App Engine application for Google Apps Marketplace soon, so please wait a bit.

I've just implemented an authentication mechanism in Kay framework for Google Apps MarketPlace. So let me introduce how to deploy a marketplace application with Kay(repository version).

I have made a few more improvements in authentication system of Kay Framework, so I slightly change this article at 4:20PM on May 13th 2010JST.

1. Create an app-id

In this example, I've created a brand new application slot called 'marketplace-app'.

2. Register a listing on the MarketPlace

Firstly, you need to register yourself as a MarketPlace Vendor.
http://www.google.com/enterprise/marketplace/

Click a link 'Become a vendor' in the bottom of the page above and you'll get an instruction.

Actually you need to click a link 'Sign in' at top-right corner of the page above, and fill the form.

Then, you can create your listing. Click a button 'Create a new listing' in your Vendor Profile page.

In this example, lets create an installable app, so check the first checkbox 'My product may be directly installed into Google Apps domains'.

You can fill other fields arbitrarily except for the field 'Manifest'.

Manifest xml here:

<?xml version="1.0" encoding="UTF-8" ?>
<ApplicationManifest xmlns="http://schemas.google.com/ApplicationManifest/2009">
    
  <!-- Support info to show in the marketplace & control panel -->
  <Support>
    <!-- URL for application setup as an optional redirect during the install -->
    <Link rel="setup" href="https://marketplace-app.appspot.com/a/${DOMAIN_NAME}/setup" />
    
    <!-- URL for application configuration, accessed from the app settings page in the control panel -->
    <Link rel="manage" href="https://marketplace-app.appspot.com/a/${DOMAIN_NAME}/admin" />

    <!-- URL explaining how customers get support. -->
    <Link rel="support" href="https://marketplace-app.appspot.com/a/${DOMAIN_NAME}/support" />
  
    <!-- URL that is displayed to admins during the deletion process, to specify policies such as data retention, how to claim accounts, etc. -->
    <Link rel="deletion-policy" href="https://marketplace-app.appspot.com/deletion-policy" />
  </Support>
    
  <!-- Name and description pulled from message bundles -->
  <Name>Marketplace sample application</Name>
  <Description>A simple application for the marketplace</Description>
  
  <!-- Show this link in Google's universal navigation for all users -->
  <Extension id="navLink" type="link">
    <Name>Sample</Name>
    <Url>https://marketplace-app.appspot.com/a/${DOMAIN_NAME}/start</Url>
    <!-- This app also uses the Calendar API -->
    <Scope ref="calendarFeed"/>
  </Extension>
  
  <!-- Declare our OpenID realm so our app is white listed -->
  <Extension id="realm" type="openIdRealm">
    <Url>https://marketplace-app.appspot.com/</Url>
  </Extension>

  <!-- Need access to the Calendar feed -->
  <Scope id="calendarFeed">
    <Url>https://www.google.com/calendar/feeds/</Url>
    <Reason>This application shows the next Calendar event.</Reason>
  </Scope>

</ApplicationManifest>

In this example, there is a calendar feed setting for 2 legged OAuth access scope. For more details with a Manifest file format, please see:
http://code.google.com/googleapps/marketplace/manifest.html

After adding your listings, you will see a preview page for your app. There is an attractive 'Add it now' button on the right, but of course, you need to implement your application before adding this app.

Instead, click a link 'My Vendor Profile' on the top-right corner and you can see your listings with a link 'View OAuth Consumer Key'.

3. Implementation

Let's create a new project with Kay management script:

$ python /some/where/kay/manage.py startproject marketplace-app
Running on Kay-0.10.0
Finished creating new project: marketplace-app.
$ cd marketplace-app
$ python manage.py startapp core

settings.py:

INSTALLED_APPS = (
  'core',
  'kay.ext.gaema',
)

APP_MOUNT_POINTS = {
  'core': '/',
}

MIDDLEWARE_CLASSES = (
  'kay.sessions.middleware.SessionMiddleware',
  'kay.auth.middleware.AuthenticationMiddleware',
)
AUTH_USER_BACKEND = "kay.auth.backends.gaema.GAEMABackend"
GAEMA_USER_MODEL = "core.models.User"

GAEMA_SECRETS = {
  'google_consumer_key': 'your consumer key here',
  'google_consumer_secret': 'your consumer key secret here',
}
IS_MARKETPLACE_APP = True

Replace GAEMA_SECRETS with actual values. You can see consumer key and consumer key secret with clicking the link 'View OAuth Consumer Key' mentioned above. The last attribute 'IS_MARKETPLACE_APP' is important for this authentication system work normaly.

core/models.py:

# -*- coding: utf-8 -*-                                                                                                                                          
# core.models                                                                                                                                                    

from google.appengine.ext import db
from kay.ext.gaema.models import GAEMAUser

# Create your models here.                                                                                                                                       

class User(GAEMAUser):
  pass

Information of users will be stored into this model.

core/urls.py:

# -*- coding: utf-8 -*-
# core.urls
#

from kay.routing import (
  ViewGroup, Rule
)

view_groups = [
  ViewGroup(
    Rule('/a/<domain_name>/setup', endpoint='domain_setup',
         view='core.views.domain_setup'),

    Rule('/a/<domain_name>/admin', endpoint='domain_admin',
         view='core.views.domain_admin'),

    Rule('/a/<domain_name>/support', endpoint='domain_support',
         view='core.views.domain_support'),

    Rule('/a/<domain_name>/start', endpoint='domain_start',
         view='core.views.domain_start'),

    Rule('/deletion_policy', endpoint='deletion_policy',
         view='core.views.deletion_policy'),
  )
]

core/views.py:

# -*- coding: utf-8 -*-                                                                                                                                          
"""                                                                                                                                                              
core.views                                                                                                                                                       
"""
from werkzeug import (
  unescape, redirect, Response,
)

from kay.utils import render_to_response
from kay.ext.gaema.utils import get_gaema_user
from kay.auth.decorators import login_required

def index(request):
  return render_to_response('core/index.html', {'message': 'Hello'})

def domain_setup(request, domain_name):
  callback = request.args.get('callback')
  if callback is None:
    return Response("No callback supplied.")
  return redirect(callback)

def domain_admin(request, domain_name):
  return Response("%s administration." % domain_name)

def domain_support(request, domain_name):
  return Response("%s support." % domain_name)

def deletion_policy(request):
  return Response("Deletion policy.")

@login_required
def domain_start(request, domain_name):
  return Response("%s start.\n%s" % (domain_name, request.user.raw_user_data))

A decorator 'login_required' takes care of OpenID/OAuth stuff, so you don't need to do anything at all. The user information is in request.user.raw_user_data.

Let's deploy it to appspot.

$ python manage.py appcfg update

You can check the application with visiting https://marketplace-app.appspot.com/a/example.com/start where example.com should be replaced with your actual domain. This domain must be registered with Google Apps and make sure that "Federated Login using OpenID" is turned on.

If the app is successfully deployed, you can see an OpenID authentication display. Let's move on.

4. Add your app to your domain

Go back to your Vendor Profile Page, and click your application title, then you will see the preview page of your application.

Let's click 'Add it now' button on the right, and enter your google apps domain, and click 'Go'.
Follow an adding application wizard like 1) Agree to terms 2) Grant data access 3) External configuration 4) Enable the app, and wait for a while, and you can see a universal navigation link 'Sample' on the top left corner of your google apps applications.

If you click the link, you can silently signed into your application because Marketplace apps are whitelisted with a particular openid_realm.

In part 2 of this article, I'll show you how to access user's calendar data with gdata library.

To be continued..

4 comments:

_generator said...

excellent , thanks for the tutorial. waiting for oauth tutorial. -- you may want to add http://blog.notdot.net/2009/11/Enforcing-data-isolation-with-CurrentUserProperty

for marketplace too.

Kapil Sachdeva said...

Thanks for this post. Was able to add my marketplace ... can see myappengineapp.appspot.com/a/mymarketplaceapp.com/[setup,admin] urls working fine. When I go to myappengineapp.appspot.com/a/mymarketplaceapp.com/start I get an error .. mentioned below ..

Traceback (most recent call last):

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/app.py", line 339, in get_response
response = view_func(request, **values)

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/utils/decorators.py", line 39, in __call__
return self.decorator(self.func)(*args, **kwargs)

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/ext/gaema/decorators.py", line 48, in inner
nexturl=request.url))

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/ext/gaema/utils.py", line 35, in create_marketplace_login_url
return url_for("gaema/marketplace_login", domain=domain)

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/utils/__init__.py", line 110, in url_for
force_external=external)

File "/base/data/home/apps/myappengineapp/1.341913087607156776/kay/lib/werkzeug/routing.py", line 1409, in build
raise BuildError(endpoint, values, method)

BuildError: ('gaema/marketplace_login', {'domain': u'mymarketplaceapp.com'}, None)

Please bear with me ... new with kay framework. Have some django experience.

Does this error means that I am missing the template files.

The code is running using the latest code from trunk.

Please suggest.

Kapil Sachdeva said...

Ignore my previous post ... forgot to add kay.ext.gemma in the apps ...

Very good stuff. Liked both Kay and what you have done with marketplace integration ... can't wait for your next post.

tmatsuo said...

Thanks for the comments.

Actually, after I wrote the article, I made few more improvements in marketplace authentication with Kay framework.

So, I'll write a revised version of the same article before we go further into OAuth stuff.