1#!/usr/bin/env python
2# Copyright (c) 2012 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
39
40def fetch_license_data(userid):
41  """Fetches the license for a given user by making an OAuth signed request
42  to the license server.
43
44  Args:
45    userid OpenID of the user you are checking access for.
46
47  Returns:
48    The server's response as text.
49  """
50  url = CONFIG['license_path'] % {
51    'server': CONFIG['license_server'],
52    'appid': CONFIG['app_id'],
53    'userid': urllib.quote_plus(userid),
54  }
55
56  oauth_token = oauth2.Token(**{
57    'key': CONFIG['oauth_token'],
58    'secret': CONFIG['oauth_token_secret']
59  })
60
61  oauth_consumer = oauth2.Consumer(**{
62    'key': CONFIG['oauth_consumer_key'],
63    'secret': CONFIG['oauth_consumer_secret']
64  })
65
66  logging.debug('Requesting %s' % url)
67  client = oauth2.Client(oauth_consumer, oauth_token)
68  resp, content = client.request(url, 'GET')
69  logging.debug('Got response code %s, content %s' % (resp, content))
70  return content
71
72
73def parse_license_data(userid):
74  """Returns the license for a given user as a structured object.
75
76  Args:
77    userid: The OpenID of the user to check.
78
79  Returns:
80    An object with the following parameters:
81      error:  True if something went wrong, False otherwise.
82      message: A descriptive message if error is True.
83      access: One of 'NO', 'FREE_TRIAL', or 'FULL' depending on the access.
84  """
85  license = {'error': False, 'message': '', 'access': 'NO'}
86  try:
87    response_text = fetch_license_data(userid)
88    try:
89      logging.debug('Attempting to JSON parse: %s' % response_text)
90      json = simplejson.loads(response_text)
91      logging.debug('Got license server response: %s' % json)
92    except ValueError:
93      logging.exception('Could not parse response as JSON: %s' % response_text)
94      license['error'] = True
95      license['message'] = 'Could not parse the license server response'
96  except DownloadError:
97    logging.exception('Could not fetch license data')
98    license['error'] = True
99    license['message'] = 'Could not fetch license data'
100
101  if json.has_key('error'):
102    license['error'] = True
103    license['message'] = json['error']['message']
104  elif json['result'] == 'YES' and json['accessLevel'] in VALID_ACCESS_LEVELS:
105    license['access'] = json['accessLevel']
106
107  return license
108
109
110class MainHandler(webapp.RequestHandler):
111  """Request handler class."""
112  def get(self):
113    """Handler for GET requests."""
114    user = users.get_current_user()
115    if user:
116      if IS_PRODUCTION:
117        # We should use federated_identity in production, since the license
118        # server requires an OpenID
119        userid = user.federated_identity()
120      else:
121        # On the dev server, we won't have access to federated_identity, so
122        # just use a default OpenID which will never return YES.
123        # If you want to test different response values on the development
124        # server, just change this default value (e.g. append '-yes' or
125        # '-trial').
126        userid = ('https://www.google.com/accounts/o8/id?'
127                  'id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
128      license_data = parse_license_data(userid)
129      template_data = {
130        'license': license_data,
131        'user_name': user.nickname(),
132        'user_id': userid,
133        'user_logout': users.create_logout_url(self.request.uri),
134      }
135    else:
136      # Force the OpenID login endpoint to be for Google accounts only, since
137      # the license server doesn't support any other type of OpenID provider.
138      login_url = users.create_login_url(dest_url='/',
139                      federated_identity='google.com/accounts/o8/id')
140      template_data = {
141        'user_login': login_url,
142      }
143
144    # Render a simple template
145    path = os.path.join(os.path.dirname(__file__), 'templates', 'index.html')
146    self.response.out.write(template.render(path, template_data))
147
148
149if __name__ == '__main__':
150  application = webapp.WSGIApplication([
151    ('/', MainHandler),
152  ], debug=False)
153  util.run_wsgi_app(application)
154