Moving over OAuth 1.0 to use Storage, and updating samples to handle token revocation.

This commit is contained in:
Joe Gregorio
2011-02-17 17:13:26 -05:00
parent 9b57dd3e3c
commit a0a52e44f2
6 changed files with 128 additions and 48 deletions

View File

@@ -10,8 +10,10 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
import pickle
from apiclient.oauth import Storage as BaseStorage
class Storage(object):
class Storage(BaseStorage):
"""Store and retrieve a single credential to and from a file."""
def __init__(self, filename):
@@ -29,6 +31,7 @@ class Storage(object):
f.close()
except:
credentials = None
credentials.set_store(self.put)
return credentials

View File

@@ -7,13 +7,13 @@ Utilities for making it easier to work with OAuth.
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
import copy
import httplib2
import logging
import oauth2 as oauth
import urllib
import urlparse
from anyjson import simplejson
try:
@@ -36,6 +36,10 @@ class MissingParameter(Error):
pass
class CredentialsInvalidError(Error):
pass
def _abstract():
raise NotImplementedError('You need to override this function')
@@ -84,6 +88,29 @@ class Flow(object):
pass
class Storage(object):
"""Base class for all Storage objects.
Store and retrieve a single credential.
"""
def get(self):
"""Retrieve credential.
Returns:
apiclient.oauth.Credentials
"""
_abstract()
def put(self, credentials):
"""Write a credential.
Args:
credentials: Credentials, the credentials to store.
"""
_abstract()
class OAuthCredentials(Credentials):
"""Credentials object for OAuth 1.0a
"""
@@ -98,6 +125,39 @@ class OAuthCredentials(Credentials):
self.consumer = consumer
self.token = token
self.user_agent = user_agent
self.store = None
# True if the credentials have been revoked
self._invalid = False
@property
def invalid(self):
"""True if the credentials are invalid, such as being revoked."""
return getattr(self, "_invalid", False)
def set_store(self, store):
"""Set the storage for the credential.
Args:
store: callable, a callable that when passed a Credential
will store the credential back to where it came from.
This is needed to store the latest access_token if it
has been revoked.
"""
self.store = store
def __getstate__(self):
"""Trim the state down to something that can be pickled.
"""
d = copy.copy(self.__dict__)
del d['store']
return d
def __setstate__(self, state):
"""Reconstitute the state of the object from being pickled.
"""
self.__dict__.update(state)
self.store = None
def authorize(self, http):
"""
@@ -148,6 +208,15 @@ class OAuthCredentials(Credentials):
response_code = resp.status
if response_code in [301, 302]:
uri = resp['location']
# Update the stored credential if it becomes invalid.
if response_code == 401:
logging.info('Access token no longer valid: %s' % content)
self._invalid = True
if self.store is not None:
self.store(self)
raise CredentialsInvalidError("Credentials are no longer valid.")
return resp, content
http.request = new_request

View File

@@ -208,7 +208,8 @@ class OAuth2Credentials(Credentials):
d = simplejson.loads(content)
if 'error' in d:
self._invalid = True
self.store(self)
if self.store is not None:
self.store(self)
except:
pass
logging.error('Failed to retrieve access token: %s' % content)

View File

@@ -14,7 +14,8 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
from apiclient.discovery import build
from apiclient.oauth import FlowThreeLegged
from apiclient.ext.authtools import run
from apiclient.ext.file import Storage
from apiclient.oauth import CredentialsInvalidError
import httplib2
import pickle
@@ -25,11 +26,8 @@ import pprint
def main():
try:
f = open("buzz.dat", "r")
credentials = pickle.loads(f.read())
f.close()
except:
credentials = Storage('buzz.dat').get()
if credentials is None or credentials.invalid == True:
buzz_discovery = build("buzz", "v1").auth_discovery()
flow = FlowThreeLegged(buzz_discovery,
@@ -49,43 +47,49 @@ def main():
developerKey="AIzaSyDRRpR3GS1F1_jKNNM9HCNd2wJQyPG3oN0")
activities = p.activities()
# Retrieve the first two activities
activitylist = activities.list(
max_results='2', scope='@self', userId='@me').execute()
print "Retrieved the first two activities"
try:
# Retrieve the first two activities
activitylist = activities.list(
max_results='2', scope='@self', userId='@me').execute()
print "Retrieved the first two activities"
# Retrieve the next two activities
if activitylist:
activitylist = activities.list_next(activitylist).execute()
print "Retrieved the next two activities"
# Retrieve the next two activities
if activitylist:
activitylist = activities.list_next(activitylist).execute()
print "Retrieved the next two activities"
# Add a new activity
new_activity_body = {
"data": {
'title': 'Testing insert',
'object': {
'content': u'Just a short note to show that insert is working. ☄',
'type': 'note'}
}
}
activity = activities.insert(userId='@me', body=new_activity_body).execute()
print "Added a new activity"
activitylist = activities.list(
max_results='2', scope='@self', userId='@me').execute()
# Add a comment to that activity
comment_body = {
"data": {
"content": "This is a comment"
# Add a new activity
new_activity_body = {
"data": {
'title': 'Testing insert',
'object': {
'content':
u'Just a short note to show that insert is working. ☄',
'type': 'note'}
}
}
item = activitylist['items'][0]
comment = p.comments().insert(
userId=item['actor']['id'], postId=item['id'], body=comment_body
).execute()
print 'Added a comment to the new activity'
pprint.pprint(comment)
}
activity = activities.insert(
userId='@me', body=new_activity_body).execute()
print "Added a new activity"
activitylist = activities.list(
max_results='2', scope='@self', userId='@me').execute()
# Add a comment to that activity
comment_body = {
"data": {
"content": "This is a comment"
}
}
item = activitylist['items'][0]
comment = p.comments().insert(
userId=item['actor']['id'], postId=item['id'], body=comment_body
).execute()
print 'Added a comment to the new activity'
pprint.pprint(comment)
except CredentialsInvalidError:
print 'Your credentials are no longer valid.'
print 'Please re-run this application to re-authorize.'
if __name__ == '__main__':
main()

View File

@@ -28,7 +28,7 @@ from apiclient.ext.file import Storage
def main():
credentials = Storage('latitude.dat').get()
if credentials is None:
if credentials is None or credentials.invalid == True:
auth_discovery = build("latitude", "v1").auth_discovery()
flow = FlowThreeLegged(auth_discovery,
# You MUST have a consumer key and secret tied to a

View File

@@ -17,15 +17,14 @@ from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
from apiclient.oauth import CredentialsInvalidError
# Uncomment to get detailed logging
#httplib2.debuglevel = 4
def main():
storage = Storage('latitude.dat')
credentials = storage.get()
credentials = Storage('latitude.dat').get()
if credentials is None or credentials.invalid:
flow = OAuth2WebServerFlow(
client_id='433807057907.apps.googleusercontent.com',
@@ -50,7 +49,11 @@ def main():
"altitude": 35
}
}
print p.currentLocation().insert(body=body).execute()
try:
print p.currentLocation().insert(body=body).execute()
except CredentialsInvalidError:
print 'Your credentials are no longer valid.'
print 'Please re-run this application to re-authorize.'
if __name__ == '__main__':
main()