ade@google.com | 60a53c0 | 2011-01-10 02:33:17 +0000 | [diff] [blame^] | 1 | # Copyright (C) 2010 Google Inc. |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
| 15 | from google.appengine.api import users |
| 16 | from google.appengine.ext import db |
| 17 | |
| 18 | import apiclient.ext.appengine |
| 19 | import logging |
| 20 | import settings |
| 21 | import simple_buzz_wrapper |
| 22 | |
| 23 | |
| 24 | class Flow(db.Model): |
| 25 | flow = apiclient.ext.appengine.FlowThreeLeggedProperty() |
| 26 | |
| 27 | |
| 28 | class Credentials(db.Model): |
| 29 | credentials = apiclient.ext.appengine.OAuthCredentialsProperty() |
| 30 | |
| 31 | |
| 32 | def oauth_required(handler_method): |
| 33 | """A decorator to require that a user has gone through the OAuth dance before accessing a handler. |
| 34 | |
| 35 | To use it, decorate your get() method like this: |
| 36 | @oauth_required |
| 37 | def get(self): |
| 38 | buzz_wrapper = oauth_handlers.build_buzz_wrapper_for_current_user() |
| 39 | user_profile_data = buzz_wrapper.get_profile() |
| 40 | self.response.out.write('Hello, ' + user_profile_data.displayName) |
| 41 | |
| 42 | We will redirect the user to the OAuth endpoint and afterwards the OAuth |
| 43 | will send the user back to the DanceFinishingHandler that you have configured. |
| 44 | This should only used for GET requests since any payload in a POST request |
| 45 | will be lost. Any parameters in the original URL will be preserved. |
| 46 | """ |
| 47 | def check_oauth_credentials(self, *args): |
| 48 | if self.request.method != 'GET': |
| 49 | raise webapp.Error('The check_oauth decorator can only be used for GET ' |
| 50 | 'requests') |
| 51 | |
| 52 | # Is this a request from the OAuth system after finishing the OAuth dance? |
| 53 | if self.request.get('oauth_verifier'): |
| 54 | user = users.get_current_user() |
| 55 | logging.debug('Finished OAuth dance for: %s' % user.email()) |
| 56 | |
| 57 | f = Flow.get_by_key_name(user.user_id()) |
| 58 | if f: |
| 59 | credentials = f.flow.step2_exchange(self.request.params) |
| 60 | c = Credentials(key_name=user.user_id(), credentials=credentials) |
| 61 | c.put() |
| 62 | |
| 63 | # We delete the flow so that a malicious actor can't pretend to be the OAuth service |
| 64 | # and replace a valid token with an invalid token |
| 65 | f.delete() |
| 66 | handler_method(self, *args) |
| 67 | return |
| 68 | |
| 69 | # Find out who the user is. If we don't know who you are then we can't |
| 70 | # look up your OAuth credentials thus we must ensure the user is logged in. |
| 71 | user = users.get_current_user() |
| 72 | if not user: |
| 73 | self.redirect(users.create_login_url(self.request.uri)) |
| 74 | return |
| 75 | |
| 76 | # Now that we know who the user is look up their OAuth credentials |
| 77 | # if we don't find the credentials then send them through the OAuth dance |
| 78 | if not Credentials.get_by_key_name(user.user_id()): |
| 79 | # TODO(ade) make this more configurable using settings.py |
| 80 | # Domain, and scope should be configurable |
| 81 | p = apiclient.discovery.build("buzz", "v1") |
| 82 | flow = apiclient.oauth.FlowThreeLegged(p.auth_discovery(), |
| 83 | consumer_key=settings.CONSUMER_KEY, |
| 84 | consumer_secret=settings.CONSUMER_SECRET, |
| 85 | user_agent='google-api-client-python-buzz-webapp/1.0', |
| 86 | domain='anonymous', |
| 87 | scope='https://www.googleapis.com/auth/buzz', |
| 88 | xoauth_displayname=settings.DISPLAY_NAME) |
| 89 | |
| 90 | # The OAuth system needs to send the user right back here so that they |
| 91 | # get to the page they originally intended to visit. |
| 92 | oauth_return_url = self.request.uri |
| 93 | authorize_url = flow.step1_get_authorize_url(oauth_return_url) |
| 94 | |
| 95 | f = Flow(key_name=user.user_id(), flow=flow) |
| 96 | f.put() |
| 97 | |
| 98 | self.redirect(authorize_url) |
| 99 | return |
| 100 | |
| 101 | # If the user already has a token then call the wrapped handler |
| 102 | handler_method(self, *args) |
| 103 | return check_oauth_credentials |
| 104 | |
| 105 | def build_buzz_wrapper_for_current_user(): |
| 106 | user = users.get_current_user() |
| 107 | credentials = Credentials.get_by_key_name(user.user_id()).credentials |
| 108 | return simple_buzz_wrapper.SimpleBuzzWrapper(api_key=settings.API_KEY, |
| 109 | credentials=credentials) |