1#!/usr/bin/python
2# Copyright (c) 2010 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from google.appengine.ext import webapp
7from google.appengine.ext.webapp import util
8from google.appengine.api import users
9from google.appengine.api import urlfetch
10from google.appengine.ext.webapp import template
11from google.appengine.api.urlfetch import DownloadError
12import oauth2
13import urllib
14import logging
15import os
16import time
17from django.utils import simplejson
18
19# Configuration
20
21CONFIG = {
22  'oauth_consumer_key': 'anonymous',
23  'oauth_consumer_secret': 'anonymous',
24  'license_server': 'https://www.googleapis.com',
25  'license_path': '%(server)s/chromewebstore/v1/licenses/%(appid)s/%(userid)s',
26  'oauth_token': 'INSERT OAUTH TOKEN HERE',
27  'oauth_token_secret': 'INSERT OAUTH TOKEN SECRET HERE',
28  'app_id': 'INSERT APPLICATION ID HERE',
29}
30
31# Check to see if the server has been deployed.  In the dev server, this
32# env variable will start with 'Development', in production, it will start with
33# 'Google App Engine'
34IS_PRODUCTION = os.environ['SERVER_SOFTWARE'].startswith('Google App Engine')
35
36# Valid access levels that may be returned by the license server.
37VALID_ACCESS_LEVELS = ['FREE_TRIAL', 'FULL']
38
39def fetch_license_data(userid):
40  """Fetches the license for a given user by making an OAuth signed request
41  to the license server.
42
43  Args:
44    userid OpenID of the user you are checking access for.
45
46  Returns:
47    The server's response as text.
48  """
49  url = CONFIG['license_path'] % {
50    'server': CONFIG['license_server'],
51    'appid': CONFIG['app_id'],
52    'userid': urllib.quote_plus(userid),
53  }
54
55  oauth_token = oauth2.Token(**{
56    'key': CONFIG['oauth_token'],
57    'secret': CONFIG['oauth_token_secret']
58  })
59
60  oauth_consumer = oauth2.Consumer(**{
61    'key': CONFIG['oauth_consumer_key'],
62    'secret': CONFIG['oauth_consumer_secret']
63  })
64
65  logging.debug('Requesting %s' % url)
66  client = oauth2.Client(oauth_consumer, oauth_token)
67  resp, content = client.request(url, 'GET')
68  logging.debug('Got response code %s, content %s' % (resp, content))
69  return content
70
71def parse_license_data(userid):
72  """Returns the license for a given user as a structured object.
73
74  Args:
75    userid: The OpenID of the user to check.
76
77  Returns:
78    An object with the following parameters:
79      error:  True if something went wrong, False otherwise.
80      message: A descriptive message if error is True.
81      access: One of 'NO', 'FREE_TRIAL', or 'FULL' depending on the access.
82  """
83  license = {'error': False, 'message': '', 'access': 'NO'}
84  try:
85    response_text = fetch_license_data(userid)
86    try:
87      logging.debug('Attempting to JSON parse: %s' % response_text)
88      json = simplejson.loads(response_text)
89      logging.debug('Got license server response: %s' % json)
90    except ValueError:
91      logging.exception('Could not parse response as JSON: %s' % response_text)
92      license['error'] = True
93      license['message'] = 'Could not parse the license server response'
94  except DownloadError:
95    logging.exception('Could not fetch license data')
96    license['error'] = True
97    license['message'] = 'Could not fetch license data'
98
99  if json.has_key('error'):
100    license['error'] = True
101    license['message'] = json['error']['message']
102  elif json['result'] == 'YES' and json['accessLevel'] in VALID_ACCESS_LEVELS:
103    license['access'] = json['accessLevel']
104
105  return license
106
107class MainHandler(webapp.RequestHandler):
108  """Request handler class."""
109  def get(self):
110    """Handler for GET requests."""
111    user = users.get_current_user()
112    if user:
113      if IS_PRODUCTION:
114        # We should use federated_identity in production, since the license
115        # server requires an OpenID
116        userid = user.federated_identity()
117      else:
118        # On the dev server, we won't have access to federated_identity, so
119        # just use a default OpenID which will never return YES.
120        # If you want to test different response values on the development
121        # server, just change this default value (e.g. append '-yes' or
122        # '-trial').
123        userid = ('https://www.google.com/accounts/o8/id?'
124                  'id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
125      license_data = parse_license_data(userid)
126      template_data = {
127        'license': license_data,
128        'user_name': user.nickname(),
129        'user_id': userid,
130        'user_logout': users.create_logout_url(self.request.uri),
131      }
132    else:
133      # Force the OpenID login endpoint to be for Google accounts only, since
134      # the license server doesn't support any other type of OpenID provider.
135      login_url = users.create_login_url(dest_url='/',
136                      federated_identity='google.com/accounts/o8/id')
137      template_data = {
138        'user_login': login_url,
139      }
140
141    # Render a simple template
142    path = os.path.join(os.path.dirname(__file__), 'templates', 'index.html')
143    self.response.out.write(template.render(path, template_data))
144
145if __name__ == '__main__':
146  application = webapp.WSGIApplication([
147    ('/', MainHandler),
148  ], debug=False)
149  util.run_wsgi_app(application)
150