Subscribe

Appirio RSS Feed
Subscribe with Bloglines

Add to Google
Subscribe in NewsGator Online

Community

Appirio Technology Blog

Showing posts with label Google Apps. Show all posts
Showing posts with label Google Apps. Show all posts

Thursday, October 16, 2008

Google Apps Auth Backend for Django

Tim Garthwaite

Google loves Python. In fact, Google's original web spider, which crawls the web to create its search index was written while Larry Page and Sergey Brin (the founders) were still graduate students at Stanford, and rumors abound that it went live written completely in Python. I learned in university that most of the Python code performed well enough that much of the code was still Python to that day (circa 2000), although much of it was highly optimized in platform-specific C. Moreover, Google's new Platform-as-a-Service (PaaS), AppEngine, which allows anyone in the world to host complete web applications "in the cloud" for free (heavy use will be charged at far below-market rates), currently supports only one language (you guessed it: Python). While Google has assured that they will release AppEngine SDKs for other languages, only Python is currently supported.

AppEngine, it can be argued, may not be ready for prime-time commercial or enterprise use, as it does not support SSL for all communication between the browser and servers. Authentication can be done safely by redirecting to a secure login page and returning with a token, but the token (and all your corporate data) would then be passed back and forth in plaintext from then on. Google has promised to add SSL support to AppEngine, but until they do, Appirio's Google Practice has begun recommending the full Django platform (on Apache or, heavens forbid, IIS) for internally developed applications, in anticipation that converting these web applications to AppEngine would be relatively painless.

The AppEngine Python SDK comes with much of the Django framework pre-installed, including its fantastic templating system. Also, the Object-Relational Mapping (ORM) system built into AppEngine is remarkably similar to the ORM that comes with Django, and the AppEngine authentication system is markably similar to its Django equivalent as well. These facts should make conversion from custom in-house Django applications to AppEngine in the future (and throwing out your pesky web servers, gaining the best features of the world's most robustly distributed compute in the world, in the process) relatively painless.

So let's say you wish to go ahead with creating Python/Django web applications in-house. Django comes with an authentication framework that allows for custom back-ends, meaning that you can test username/password combinations against an arbitrary back-end system, such as Active Directory or any other LDAP system, or even against users stored in a custom database. For one of Appirio's clients who is fully embracing the cloud, including Google Mail, Calendar, and Docs corporate-wide, it made the most sense for a certain application to authenticate against Google Apps itself using Google's Apps Provisioning API. Here's how I accomplished this.

First, you must create the back-end Python class. For example purposes, I have created a 'mymodule' directory (anywhere in my Python path) containing an empty __init__.py file (telling Python to treat this directory as a module) and the file django_backend.py. Of course, you must replace "mydomain.com" with your own domain, and as your Python code base grows, you should adhere to a more logical standard for where you place your libraries. It would make sense to think about this and begin now so you won't have to refactor your code. In my system, the class file is in the 'appirio.google' module. Here are the contents of this file:

from django.contrib.auth.models include User, check_password
from gdata.apps.service include AppsService
from gdata.docs.service include DocsService
DOMAIN = 'mydomain.com'
ADMIN_USERNAME = 'admin_user'
ADMIN_PASSWORD = 'p@s$w3rd'
class GoogleAppsBackend:
""" Authenticate against Google Apps """
def authenticate(self, username=None, password=None):
user = None
email = '%s@%s' % (username, DOMAIN)
admin_email = '%s@%s' % (ADMIN_USERNAME, DOMAIN)
try:
# Check user's password
gdocs = gdata.docs.service.DocsService()
gdocs.email = email
gdocs.password = password
gdocs.ProgrammaticLogin()
# Get the user object
gapps = AppsService(domain=DOMAIN)
gapps.ClientLogin(username=admin_email,
password=admin_password,
account_type='HOSTED', service='apps')
guser = gapps.RetreiveUser(username)
user = User.objects.get_or_create(username=username)
user.email = email
user.last_name = guser.name.family_name
user.first_name = guser.name.given_name
user.is_active = not guser.login.suspended == 'true'
user.is_superuser = guser.login.admin == 'true'
user.is_staff = user.is_superuser
user.save()
except:
pass

return user

def get_user(self, user_id):

user = None

try:

user = User.objects.get(pk=user_id)

except:

pass

return user

Let's briefly review this code. authenticate() uses the GData Python library to ensure the username and password match with the actual Google Apps account. Since you need an administrator account to use the Provisioning API, I chose an arbitrary user-accessible API (Google Docs) to verify the user's password. If the password doesn't match, an exception is thrown, None is returned, and the login fails. If it does match, we log in to the Provisioning API with admin credentials to get the Google Apps user object, guser. Then, using a built-in helper method, we attempt to get the Django User object with matching username, or create a new one. Either way, we take the opportunity to update the User object with data from Apps. get_user() is a required function (as we are creating a class to meet a "duck-type" interface, rather than inheritance). We simply return a Django User, if one exists, or None.

Finally, to enable this back-end, you must modify the site's settings.py file, ensuring 'django.contrib.auth' is included in INSTALLED_APPS, and adding 'mymodule.django_backend.GoogleAppsBackend' to AUTHENTICATION_BACKENDS. You can now test logging into your site as Google Apps users. If you have enabled 'django.contrib.admin', you can then login to your site's admin console and see that these users were automatically added into your Django auth system. You could also easily create a web page to list these users by passing 'users': User.objects.all() into a template and writing template code such as:

<ul>{%foreach user in users%}<li>{{user.email}}</li>{%endfor%}</ul>

We hope you find this code useful. Feel free to use any or all of it in your own Django web applications. If you do, please let us know in the comments!

Wednesday, October 8, 2008

Calendar Resource Management with the Google Data API

Matt Pruden

In many enterprises, there is no piece of real estate more scarce than an unoccupied conference room. With so much importance placed on conference rooms, their rigorous management is critical to a successful Google Apps deployment.

While Google Calendar offers a flexible system for reserving conference rooms, projectors, scooters, or any other shared resource, it does not provide a documented API for creating, updating, and deleting resources. Instead, you must manually manage resources through the Google Apps control panel. Manual management may work for a small number of resources but becomes unscalable when managing thousands.

However, creative developers can find just such a Google Data (GData) API for provisioning resources. In this post, we'll explore how to create, read, update, and delete calendar resources using GData through cURL, the commonly available command line HTTP client.

Discovering Calendar Resource support in GData.


Each type of entry in Google, whether a spreadsheet row, user account, or nickname has a collection URL. In true REST fashion, a GET request to the collection URL will return a list of entries. For example, an GET request to http://www.google.com/calendar/feeds/default/private/full will return a feed of calendar event entries. Likewise, a POST to this URL will add a new event entry to a calendar. So, to retrieve and create resources, we first need to discover the collection URL for calendar resources.

A calendar resource has many of the same characteristics as a user. For example, a calendar resource can be a meeting attendee and can be browsed by clicking "check guest and resource availability" in the Calendar user interface. Also, a calendar resource isn't tied to a particular user when it is created. It is reasonable to believe that managing calendar resources through the API might closely mimic managing users through the provisioning API.

In the provisioning API, the collection URL for user accounts looks like this: https://apps-apis.google.com/a/feeds/domain/user/2.0. What if we change user to resource resulting in a URL like this: https://apps-apis.google.com/a/feeds/domain/resource/2.0? The example below uses the cURL application to send a GET request to the new URL. For details on using cURL with GData, see Google's documentation.

curl -s -k --header "Authorization: GoogleLogin auth=DQAAAH4AA" https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0 | tidy -xml -indent -quiet
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:gCal="http://schemas.google.com/gCal/2005" xmlns:apps="http://schemas.google.com/apps/2006" xmlns:gd="http://schemas.google.com/g/2005"> <id>https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0</id> <updated>1970-01-01T00:00:00.000Z</updated> <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#resource"/> <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0"/> <link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0"/> <link rel="self" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0"/> <openSearch:startIndex>1</openSearch:startIndex> <entry> <id>https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0/-81411918824</id> <updated>1970-01-01T00:00:00.000Z</updated> <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#resource"/> <title type="text">Bldg 3, room 201</title> <link rel="self" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0/-81411918824"/> <link rel="edit" type="application/atom+xml" href="https://apps-apis.google.com/a/feeds/mydomain.com/resource/2.0/-81411918824"/> <gd:who valueString="Bldg 3, room 201" email="mydomain.com_2d3831343131393138383234@resource.calendar.google.com"> <gCal:resource id="-81411918824"/> </gd:who> </entry> </feed>

We've found the collection URL for calendar resources! Now, we just need to determine the XML schema for an individual resource. A hour of trial and error results in the following schema:

<?xml version='1.0' encoding='UTF-8'?> <ns0:entry xmlns:ns0="http://www.w3.org/2005/Atom"> <ns0:category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/apps/2006#resource" /> <ns1:who valueString="long name" xmlns:ns1="http://schemas.google.com/g/2005"> <ns2:resource id="short name" xmlns:ns2="http://schemas.google.com/gCal/2005" /> </ns1:who> </ns0:entry>

Since Google already does a great job of explaining the GData API, this post will not repeat that information. Instead, you can use the collection URL and entry schema in the same fashion as the other GData APIs to create, read, update, and delete calendar resources.

Wednesday, August 27, 2008

Defaulting your mailto: links to Google Apps in Firefox 3

Tim Parker

If you're using Google Apps and Firefox and would like to default email links to open in Google Apps follow the steps below.

First, make sure you're using Firefox 3. Open Firefox, and enter about:config into the address bar. This will bring up a warning message, click on the "I'll be careful, I promise!" button.

Capture

First, we need to edit the option for web pages to register themselves as protocol handlers, so enter gecko.handler in the filter bar and select the option highlighted below.

gecko.handlerService.allowRegisterFromDifferenceHost

Capture2

Make sure this option is set to True (You can do so by double clicking that entry in the browser). This allows mailto: links to forward to web based email providers such as Google Apps. If you wanted to set webcal: links to a web based calendar like Google Calendar you would need this setting enabled as well.

Now that we've enabled this option we need to register Google Apps as a handler. To do this, we need to execute a single line of JavaScript. In the address bar, copy and paste the following:

javascript:window.navigator.registerProtocolHandler('mailto','https://mail.google.com/a/yourdomain.com/mail/?extsrc=mailto&url=%s','Google Apps')

Note that you will need to replace yourdomain.com with your actual domain name. Firefox will prompt you to add an application. Click "Add Application."

Capture3 Finally, Navigate to Tools / Options / Applications and set Google Apps as your default for mailto.

8-27-2008 9-57-15 AM