blob: f8fc9d4cb9db81fa3c7f6e19bd3061bf16fba275 [file] [log] [blame]
ade@google.com60a53c02011-01-10 02:33:17 +00001# 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
15from google.appengine.api import users
16from google.appengine.ext import db
17
18import apiclient.ext.appengine
19import logging
ade@google.com60a53c02011-01-10 02:33:17 +000020import simple_buzz_wrapper
21
22
23class Flow(db.Model):
24 flow = apiclient.ext.appengine.FlowThreeLeggedProperty()
25
26
27class Credentials(db.Model):
28 credentials = apiclient.ext.appengine.OAuthCredentialsProperty()
29
30
ade@google.comdcdd0382011-01-11 23:01:45 +000031class oauth_required(object):
32 def __init__(self, *decorator_args, **decorator_kwargs):
33 """A decorator to require that a user has gone through the OAuth dance before accessing a handler.
ade@google.com60a53c02011-01-10 02:33:17 +000034
ade@google.comdcdd0382011-01-11 23:01:45 +000035 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)
ade@google.com60a53c02011-01-10 02:33:17 +000041
ade@google.comdcdd0382011-01-11 23:01:45 +000042 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
45 This should only used for GET requests since any payload in a POST request
46 will be lost. Any parameters in the original URL will be preserved.
47 """
48 self.decorator_args = decorator_args
49 self.decorator_kwargs = decorator_kwargs
50
51 def __call__(self, handler_method):
52 def check_oauth_credentials_wrapper(*args, **kwargs):
53 handler_instance = args[0]
54 # TODO(ade) Add support for POST requests
55 if handler_instance.request.method != 'GET':
56 raise webapp.Error('The check_oauth decorator can only be used for GET '
57 'requests')
ade@google.com60a53c02011-01-10 02:33:17 +000058
ade@google.comdcdd0382011-01-11 23:01:45 +000059 # Is this a request from the OAuth system after finishing the OAuth dance?
60 if handler_instance.request.get('oauth_verifier'):
61 user = users.get_current_user()
62 logging.debug('Finished OAuth dance for: %s' % user.email())
ade@google.com60a53c02011-01-10 02:33:17 +000063
ade@google.comdcdd0382011-01-11 23:01:45 +000064 f = Flow.get_by_key_name(user.user_id())
65 if f:
66 credentials = f.flow.step2_exchange(handler_instance.request.params)
67 c = Credentials(key_name=user.user_id(), credentials=credentials)
68 c.put()
ade@google.com60a53c02011-01-10 02:33:17 +000069
ade@google.comdcdd0382011-01-11 23:01:45 +000070 # We delete the flow so that a malicious actor can't pretend to be the OAuth service
71 # and replace a valid token with an invalid token
72 f.delete()
73
74 handler_method(*args)
75 return
ade@google.com60a53c02011-01-10 02:33:17 +000076
ade@google.comdcdd0382011-01-11 23:01:45 +000077 # Find out who the user is. If we don't know who you are then we can't
78 # look up your OAuth credentials thus we must ensure the user is logged in.
79 user = users.get_current_user()
80 if not user:
81 handler_instance.redirect(users.create_login_url(handler_instance.request.uri))
82 return
ade@google.com60a53c02011-01-10 02:33:17 +000083
ade@google.comdcdd0382011-01-11 23:01:45 +000084 # Now that we know who the user is look up their OAuth credentials
85 # if we don't find the credentials then send them through the OAuth dance
86 if not Credentials.get_by_key_name(user.user_id()):
87
88 # Set up the default arguments and override them with whatever values have been given to the decorator
89 flow_settings = {
90 'consumer_key' : 'anonymous',
91 'consumer_secret' : 'anonymous',
92 'user_agent' : 'google-api-client-python-buzz-webapp/1.0',
93 'domain' : 'anonymous',
94 'scope' : 'https://www.googleapis.com/auth/buzz',
95 'xoauth_display_name' : 'Default Display Name For OAuth Application'
96 }
97
98 flow_settings.update(self.decorator_kwargs)
99
100 p = apiclient.discovery.build("buzz", "v1")
101 flow = apiclient.oauth.FlowThreeLegged(p.auth_discovery(),
102 consumer_key=flow_settings['consumer_key'],
103 consumer_secret=flow_settings['consumer_secret'],
104 user_agent=flow_settings['user_agent'],
105 domain=flow_settings['domain'],
106 scope=flow_settings['scope'],
107 xoauth_displayname=flow_settings['xoauth_display_name'])
ade@google.com60a53c02011-01-10 02:33:17 +0000108
ade@google.comdcdd0382011-01-11 23:01:45 +0000109 # The OAuth system needs to send the user right back here so that they
110 # get to the page they originally intended to visit.
111 oauth_return_url = handler_instance.request.uri
112 authorize_url = flow.step1_get_authorize_url(oauth_return_url)
ade@google.com60a53c02011-01-10 02:33:17 +0000113
ade@google.comdcdd0382011-01-11 23:01:45 +0000114 f = Flow(key_name=user.user_id(), flow=flow)
115 f.put()
ade@google.com60a53c02011-01-10 02:33:17 +0000116
ade@google.comdcdd0382011-01-11 23:01:45 +0000117 handler_instance.redirect(authorize_url)
118 return
ade@google.com60a53c02011-01-10 02:33:17 +0000119
ade@google.comdcdd0382011-01-11 23:01:45 +0000120 # If the user already has a token then call the wrapped handler
121 handler_method(*args)
122 return check_oauth_credentials_wrapper
ade@google.com60a53c02011-01-10 02:33:17 +0000123
ade@google.comdcdd0382011-01-11 23:01:45 +0000124def build_buzz_wrapper_for_current_user(api_key):
ade@google.com60a53c02011-01-10 02:33:17 +0000125 user = users.get_current_user()
126 credentials = Credentials.get_by_key_name(user.user_id()).credentials
ade@google.comdcdd0382011-01-11 23:01:45 +0000127 return simple_buzz_wrapper.SimpleBuzzWrapper(api_key=api_key,
ade@google.com60a53c02011-01-10 02:33:17 +0000128 credentials=credentials)