soundcloud python/php apis, recorder python script so far.
This commit is contained in:
parent
b3e111b0a0
commit
f68a8f67ea
109 changed files with 24297 additions and 10 deletions
0
python_apps/soundcloud-api/oauth/__init__.py
Normal file
0
python_apps/soundcloud-api/oauth/__init__.py
Normal file
157
python_apps/soundcloud-api/oauth/example/client.py
Normal file
157
python_apps/soundcloud-api/oauth/example/client.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
'''
|
||||
Example consumer.
|
||||
'''
|
||||
import httplib
|
||||
import time
|
||||
import oauth.oauth as oauth
|
||||
import webbrowser
|
||||
from scapi import util
|
||||
|
||||
SERVER = 'sandbox-soundcloud.com' # Change to soundcloud.com to reach the live site
|
||||
PORT = 80
|
||||
|
||||
REQUEST_TOKEN_URL = 'http://api.' + SERVER + '/oauth/request_token'
|
||||
ACCESS_TOKEN_URL = 'http://api.' + SERVER + '/oauth/access_token'
|
||||
AUTHORIZATION_URL = 'http://' + SERVER + '/oauth/authorize'
|
||||
|
||||
CALLBACK_URL = ''
|
||||
RESOURCE_URL = "http://api." + SERVER + "/me"
|
||||
|
||||
# key and secret granted by the service provider for this consumer application - same as the MockOAuthDataStore
|
||||
CONSUMER_KEY = 'JysXkO8ErA4EluFnF5nWg'
|
||||
CONSUMER_SECRET = 'fauVjm61niGckeufkmMvgUo77oWzRHdMmeylJblHk'
|
||||
|
||||
# example client using httplib with headers
|
||||
class SimpleOAuthClient(oauth.OAuthClient):
|
||||
|
||||
def __init__(self, server, port=httplib.HTTP_PORT, request_token_url='', access_token_url='', authorization_url=''):
|
||||
self.server = server
|
||||
self.port = port
|
||||
self.request_token_url = request_token_url
|
||||
self.access_token_url = access_token_url
|
||||
self.authorization_url = authorization_url
|
||||
self.connection = httplib.HTTPConnection("%s:%d" % (self.server, self.port))
|
||||
|
||||
def fetch_request_token(self, oauth_request):
|
||||
# via headers
|
||||
# -> OAuthToken
|
||||
print oauth_request.to_url()
|
||||
#self.connection.request(oauth_request.http_method, self.request_token_url, headers=oauth_request.to_header())
|
||||
self.connection.request(oauth_request.http_method, oauth_request.to_url())
|
||||
response = self.connection.getresponse()
|
||||
print "response status", response.status
|
||||
return oauth.OAuthToken.from_string(response.read())
|
||||
|
||||
def fetch_access_token(self, oauth_request):
|
||||
# via headers
|
||||
# -> OAuthToken
|
||||
|
||||
# This should proably be elsewhere but stays here for now
|
||||
oauth_request.set_parameter("oauth_signature", util.escape(oauth_request.get_parameter("oauth_signature")))
|
||||
self.connection.request(oauth_request.http_method, self.access_token_url, headers=oauth_request.to_header())
|
||||
response = self.connection.getresponse()
|
||||
resp = response.read()
|
||||
print "*" * 90
|
||||
print "response:", resp
|
||||
print "*" * 90
|
||||
|
||||
return oauth.OAuthToken.from_string(resp)
|
||||
|
||||
def authorize_token(self, oauth_request):
|
||||
webbrowser.open(oauth_request.to_url())
|
||||
raw_input("press return when authorizing is finished")
|
||||
|
||||
return
|
||||
|
||||
# via url
|
||||
# -> typically just some okay response
|
||||
self.connection.request(oauth_request.http_method, oauth_request.to_url())
|
||||
response = self.connection.getresponse()
|
||||
return response.read()
|
||||
|
||||
def access_resource(self, oauth_request):
|
||||
print "resource url:", oauth_request.to_url()
|
||||
webbrowser.open(oauth_request.to_url())
|
||||
|
||||
return
|
||||
|
||||
# via post body
|
||||
# -> some protected resources
|
||||
self.connection.request('GET', oauth_request.to_url())
|
||||
response = self.connection.getresponse()
|
||||
return response.read()
|
||||
|
||||
def run_example():
|
||||
|
||||
# setup
|
||||
print '** OAuth Python Library Example **'
|
||||
client = SimpleOAuthClient(SERVER, PORT, REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZATION_URL)
|
||||
consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
|
||||
signature_method_plaintext = oauth.OAuthSignatureMethod_PLAINTEXT()
|
||||
signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
|
||||
pause()
|
||||
# get request token
|
||||
print '* Obtain a request token ...'
|
||||
pause()
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, http_url=client.request_token_url)
|
||||
#oauth_request.sign_request(signature_method_plaintext, consumer, None)
|
||||
oauth_request.sign_request(signature_method_hmac_sha1, consumer, None)
|
||||
|
||||
print 'REQUEST (via headers)'
|
||||
print 'parameters: %s' % str(oauth_request.parameters)
|
||||
pause()
|
||||
#import pdb; pdb.set_trace()
|
||||
|
||||
token = client.fetch_request_token(oauth_request)
|
||||
print 'GOT'
|
||||
print 'key: %s' % str(token.key)
|
||||
print 'secret: %s' % str(token.secret)
|
||||
pause()
|
||||
|
||||
print '* Authorize the request token ...'
|
||||
pause()
|
||||
oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, callback=CALLBACK_URL, http_url=client.authorization_url)
|
||||
print 'REQUEST (via url query string)'
|
||||
print 'parameters: %s' % str(oauth_request.parameters)
|
||||
pause()
|
||||
# this will actually occur only on some callback
|
||||
response = client.authorize_token(oauth_request)
|
||||
print 'GOT'
|
||||
print response
|
||||
pause()
|
||||
|
||||
# get access token
|
||||
print '* Obtain an access token ...'
|
||||
pause()
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, http_url=client.access_token_url)
|
||||
oauth_request.sign_request(signature_method_hmac_sha1, consumer, token)
|
||||
print 'REQUEST (via headers)'
|
||||
print 'parameters: %s' % str(oauth_request.parameters)
|
||||
pause()
|
||||
token = client.fetch_access_token(oauth_request)
|
||||
print 'GOT'
|
||||
print 'key: %s' % str(token.key)
|
||||
print 'secret: %s' % str(token.secret)
|
||||
pause()
|
||||
|
||||
# access some protected resources
|
||||
print '* Access protected resources ...'
|
||||
pause()
|
||||
parameters = {}
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, http_method='GET', http_url=RESOURCE_URL, parameters=parameters)
|
||||
oauth_request.sign_request(signature_method_hmac_sha1, consumer, token)
|
||||
print 'REQUEST (via get body)'
|
||||
print 'parameters: %s' % str(oauth_request.parameters)
|
||||
pause()
|
||||
params = client.access_resource(oauth_request)
|
||||
print 'GOT'
|
||||
print 'non-oauth parameters: %s' % params
|
||||
pause()
|
||||
|
||||
def pause():
|
||||
print ''
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_example()
|
||||
print 'Done.'
|
167
python_apps/soundcloud-api/oauth/example/server.py
Normal file
167
python_apps/soundcloud-api/oauth/example/server.py
Normal file
|
@ -0,0 +1,167 @@
|
|||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
import urllib
|
||||
|
||||
import oauth.oauth as oauth
|
||||
|
||||
REQUEST_TOKEN_URL = 'https://photos.example.net/request_token'
|
||||
ACCESS_TOKEN_URL = 'https://photos.example.net/access_token'
|
||||
AUTHORIZATION_URL = 'https://photos.example.net/authorize'
|
||||
RESOURCE_URL = 'http://photos.example.net/photos'
|
||||
REALM = 'http://photos.example.net/'
|
||||
|
||||
# example store for one of each thing
|
||||
class MockOAuthDataStore(oauth.OAuthDataStore):
|
||||
|
||||
def __init__(self):
|
||||
self.consumer = oauth.OAuthConsumer('key', 'secret')
|
||||
self.request_token = oauth.OAuthToken('requestkey', 'requestsecret')
|
||||
self.access_token = oauth.OAuthToken('accesskey', 'accesssecret')
|
||||
self.nonce = 'nonce'
|
||||
|
||||
def lookup_consumer(self, key):
|
||||
if key == self.consumer.key:
|
||||
return self.consumer
|
||||
return None
|
||||
|
||||
def lookup_token(self, token_type, token):
|
||||
token_attrib = getattr(self, '%s_token' % token_type)
|
||||
if token == token_attrib.key:
|
||||
return token_attrib
|
||||
return None
|
||||
|
||||
def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
|
||||
if oauth_token and oauth_consumer.key == self.consumer.key and (oauth_token.key == self.request_token.key or token.key == self.access_token.key) and nonce == self.nonce:
|
||||
return self.nonce
|
||||
else:
|
||||
raise oauth.OAuthError('Nonce not found: %s' % str(nonce))
|
||||
return None
|
||||
|
||||
def fetch_request_token(self, oauth_consumer):
|
||||
if oauth_consumer.key == self.consumer.key:
|
||||
return self.request_token
|
||||
return None
|
||||
|
||||
def fetch_access_token(self, oauth_consumer, oauth_token):
|
||||
if oauth_consumer.key == self.consumer.key and oauth_token.key == self.request_token.key:
|
||||
# want to check here if token is authorized
|
||||
# for mock store, we assume it is
|
||||
return self.access_token
|
||||
return None
|
||||
|
||||
def authorize_request_token(self, oauth_token):
|
||||
if oauth_token.key == self.request_token.key:
|
||||
# authorize the request token in the store
|
||||
# for mock store, do nothing
|
||||
return self.request_token
|
||||
return None
|
||||
|
||||
class RequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.oauth_server = oauth.OAuthServer(MockOAuthDataStore())
|
||||
self.oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT())
|
||||
self.oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
|
||||
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
|
||||
|
||||
# example way to send an oauth error
|
||||
def send_oauth_error(self, err=None):
|
||||
# send a 401 error
|
||||
self.send_error(401, str(err.message))
|
||||
# return the authenticate header
|
||||
header = oauth.build_authenticate_header(realm=REALM)
|
||||
for k, v in header.iteritems():
|
||||
self.send_header(k, v)
|
||||
|
||||
def do_GET(self):
|
||||
|
||||
# debug info
|
||||
#print self.command, self.path, self.headers
|
||||
|
||||
# get the post data (if any)
|
||||
postdata = None
|
||||
if self.command == 'POST':
|
||||
try:
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
postdata = self.rfile.read(length)
|
||||
except:
|
||||
pass
|
||||
|
||||
# construct the oauth request from the request parameters
|
||||
oauth_request = oauth.OAuthRequest.from_request(self.command, self.path, headers=self.headers, postdata=postdata)
|
||||
|
||||
# request token
|
||||
if self.path.startswith(REQUEST_TOKEN_URL):
|
||||
try:
|
||||
# create a request token
|
||||
token = self.oauth_server.fetch_request_token(oauth_request)
|
||||
# send okay response
|
||||
self.send_response(200, 'OK')
|
||||
self.end_headers()
|
||||
# return the token
|
||||
self.wfile.write(token.to_string())
|
||||
except oauth.OAuthError, err:
|
||||
self.send_oauth_error(err)
|
||||
return
|
||||
|
||||
# user authorization
|
||||
if self.path.startswith(AUTHORIZATION_URL):
|
||||
try:
|
||||
# get the request token
|
||||
token = self.oauth_server.fetch_request_token(oauth_request)
|
||||
callback = self.oauth_server.get_callback(oauth_request)
|
||||
# send okay response
|
||||
self.send_response(200, 'OK')
|
||||
self.end_headers()
|
||||
# return the callback url (to show server has it)
|
||||
self.wfile.write('callback: %s' %callback)
|
||||
# authorize the token (kind of does nothing for now)
|
||||
token = self.oauth_server.authorize_token(token)
|
||||
self.wfile.write('\n')
|
||||
# return the token key
|
||||
token_key = urllib.urlencode({'oauth_token': token.key})
|
||||
self.wfile.write('token key: %s' % token_key)
|
||||
except oauth.OAuthError, err:
|
||||
self.send_oauth_error(err)
|
||||
return
|
||||
|
||||
# access token
|
||||
if self.path.startswith(ACCESS_TOKEN_URL):
|
||||
try:
|
||||
# create an access token
|
||||
token = self.oauth_server.fetch_access_token(oauth_request)
|
||||
# send okay response
|
||||
self.send_response(200, 'OK')
|
||||
self.end_headers()
|
||||
# return the token
|
||||
self.wfile.write(token.to_string())
|
||||
except oauth.OAuthError, err:
|
||||
self.send_oauth_error(err)
|
||||
return
|
||||
|
||||
# protected resources
|
||||
if self.path.startswith(RESOURCE_URL):
|
||||
try:
|
||||
# verify the request has been oauth authorized
|
||||
consumer, token, params = self.oauth_server.verify_request(oauth_request)
|
||||
# send okay response
|
||||
self.send_response(200, 'OK')
|
||||
self.end_headers()
|
||||
# return the extra parameters - just for something to return
|
||||
self.wfile.write(str(params))
|
||||
except oauth.OAuthError, err:
|
||||
self.send_oauth_error(err)
|
||||
return
|
||||
|
||||
def do_POST(self):
|
||||
return self.do_GET()
|
||||
|
||||
def main():
|
||||
try:
|
||||
server = HTTPServer(('', 8080), RequestHandler)
|
||||
print 'Test server running...'
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
server.socket.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
505
python_apps/soundcloud-api/oauth/oauth.py
Normal file
505
python_apps/soundcloud-api/oauth/oauth.py
Normal file
|
@ -0,0 +1,505 @@
|
|||
import cgi
|
||||
import urllib
|
||||
import time
|
||||
import random
|
||||
import urlparse
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
|
||||
VERSION = '1.0' # Hi Blaine!
|
||||
HTTP_METHOD = 'GET'
|
||||
SIGNATURE_METHOD = 'PLAINTEXT'
|
||||
|
||||
# Generic exception class
|
||||
class OAuthError(RuntimeError):
|
||||
def __init__(self, message='OAuth error occured'):
|
||||
self.message = message
|
||||
|
||||
# optional WWW-Authenticate header (401 error)
|
||||
def build_authenticate_header(realm=''):
|
||||
return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
|
||||
|
||||
# url escape
|
||||
def escape(s):
|
||||
# escape '/' too
|
||||
return urllib.quote(s, safe='')
|
||||
|
||||
# util function: current timestamp
|
||||
# seconds since epoch (UTC)
|
||||
def generate_timestamp():
|
||||
return int(time.time())
|
||||
|
||||
# util function: nonce
|
||||
# pseudorandom number
|
||||
def generate_nonce(length=8):
|
||||
return ''.join(str(random.randint(0, 9)) for i in range(length))
|
||||
|
||||
# OAuthConsumer is a data type that represents the identity of the Consumer
|
||||
# via its shared secret with the Service Provider.
|
||||
class OAuthConsumer(object):
|
||||
key = None
|
||||
secret = None
|
||||
|
||||
def __init__(self, key, secret):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
|
||||
# OAuthToken is a data type that represents an End User via either an access
|
||||
# or request token.
|
||||
class OAuthToken(object):
|
||||
# access tokens and request tokens
|
||||
key = None
|
||||
secret = None
|
||||
|
||||
'''
|
||||
key = the token
|
||||
secret = the token secret
|
||||
'''
|
||||
def __init__(self, key, secret):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
|
||||
def to_string(self):
|
||||
return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret})
|
||||
|
||||
# return a token from something like:
|
||||
# oauth_token_secret=digg&oauth_token=digg
|
||||
@staticmethod
|
||||
def from_string(s):
|
||||
params = cgi.parse_qs(s, keep_blank_values=False)
|
||||
key = params['oauth_token'][0]
|
||||
secret = params['oauth_token_secret'][0]
|
||||
return OAuthToken(key, secret)
|
||||
|
||||
def __str__(self):
|
||||
return self.to_string()
|
||||
|
||||
# OAuthRequest represents the request and can be serialized
|
||||
class OAuthRequest(object):
|
||||
'''
|
||||
OAuth parameters:
|
||||
- oauth_consumer_key
|
||||
- oauth_token
|
||||
- oauth_signature_method
|
||||
- oauth_signature
|
||||
- oauth_timestamp
|
||||
- oauth_nonce
|
||||
- oauth_version
|
||||
... any additional parameters, as defined by the Service Provider.
|
||||
'''
|
||||
parameters = None # oauth parameters
|
||||
http_method = HTTP_METHOD
|
||||
http_url = None
|
||||
version = VERSION
|
||||
|
||||
def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
self.http_method = http_method
|
||||
self.http_url = http_url
|
||||
self.parameters = parameters or {}
|
||||
|
||||
def set_parameter(self, parameter, value):
|
||||
self.parameters[parameter] = value
|
||||
|
||||
def get_parameter(self, parameter):
|
||||
try:
|
||||
return self.parameters[parameter]
|
||||
except:
|
||||
raise OAuthError('Parameter not found: %s' % parameter)
|
||||
|
||||
def _get_timestamp_nonce(self):
|
||||
return self.get_parameter('oauth_timestamp'), self.get_parameter('oauth_nonce')
|
||||
|
||||
# get any non-oauth parameters
|
||||
def get_nonoauth_parameters(self):
|
||||
parameters = {}
|
||||
for k, v in self.parameters.iteritems():
|
||||
# ignore oauth parameters
|
||||
if k.find('oauth_') < 0:
|
||||
parameters[k] = v
|
||||
return parameters
|
||||
|
||||
# serialize as a header for an HTTPAuth request
|
||||
def to_header(self, realm=''):
|
||||
auth_header = 'OAuth realm="%s"' % realm
|
||||
# add the oauth parameters
|
||||
if self.parameters:
|
||||
for k, v in self.parameters.iteritems():
|
||||
auth_header += ',\n\t %s="%s"' % (k, v)
|
||||
return {'Authorization': auth_header}
|
||||
|
||||
# serialize as post data for a POST request
|
||||
def to_postdata(self):
|
||||
return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems())
|
||||
|
||||
# serialize as a url for a GET request
|
||||
def to_url(self):
|
||||
return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
|
||||
|
||||
# return a string that consists of all the parameters that need to be signed
|
||||
def get_normalized_parameters(self):
|
||||
params = self.parameters
|
||||
try:
|
||||
# exclude the signature if it exists
|
||||
del params['oauth_signature']
|
||||
except:
|
||||
pass
|
||||
key_values = params.items()
|
||||
# sort lexicographically, first after key, then after value
|
||||
key_values.sort()
|
||||
# combine key value pairs in string and escape
|
||||
return '&'.join('%s=%s' % (str(k), str(p)) for k, p in key_values)
|
||||
|
||||
# just uppercases the http method
|
||||
def get_normalized_http_method(self):
|
||||
return self.http_method.upper()
|
||||
|
||||
# parses the url and rebuilds it to be scheme://host/path
|
||||
def get_normalized_http_url(self):
|
||||
parts = urlparse.urlparse(self.http_url)
|
||||
url_string = '%s://%s%s' % (parts.scheme, parts.netloc, parts.path)
|
||||
return url_string
|
||||
|
||||
# set the signature parameter to the result of build_signature
|
||||
def sign_request(self, signature_method, consumer, token):
|
||||
# set the signature method
|
||||
self.set_parameter('oauth_signature_method', signature_method.get_name())
|
||||
# set the signature
|
||||
self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token))
|
||||
|
||||
def build_signature(self, signature_method, consumer, token):
|
||||
# call the build signature method within the signature method
|
||||
return signature_method.build_signature(self, consumer, token)
|
||||
|
||||
@staticmethod
|
||||
def from_request(http_method, http_url, headers=None, postdata=None, parameters=None):
|
||||
|
||||
# let the library user override things however they'd like, if they know
|
||||
# which parameters to use then go for it, for example XMLRPC might want to
|
||||
# do this
|
||||
if parameters is not None:
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
|
||||
# from the headers
|
||||
if headers is not None:
|
||||
try:
|
||||
auth_header = headers['Authorization']
|
||||
# check that the authorization header is OAuth
|
||||
auth_header.index('OAuth')
|
||||
# get the parameters from the header
|
||||
parameters = OAuthRequest._split_header(auth_header)
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
except:
|
||||
pass
|
||||
|
||||
# from the parameter string (post body)
|
||||
if http_method == 'POST' and postdata is not None:
|
||||
parameters = OAuthRequest._split_url_string(postdata)
|
||||
|
||||
# from the url string
|
||||
elif http_method == 'GET':
|
||||
param_str = urlparse.urlparse(http_url).query
|
||||
parameters = OAuthRequest._split_url_string(param_str)
|
||||
|
||||
if parameters:
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
|
||||
raise OAuthError('Missing all OAuth parameters. OAuth parameters must be in the headers, post body, or url.')
|
||||
|
||||
@staticmethod
|
||||
def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
if not parameters:
|
||||
parameters = {}
|
||||
|
||||
defaults = {
|
||||
'oauth_consumer_key': oauth_consumer.key,
|
||||
'oauth_timestamp': generate_timestamp(),
|
||||
'oauth_nonce': generate_nonce(),
|
||||
'oauth_version': OAuthRequest.version,
|
||||
}
|
||||
|
||||
defaults.update(parameters)
|
||||
parameters = defaults
|
||||
|
||||
if token:
|
||||
parameters['oauth_token'] = token.key
|
||||
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
|
||||
@staticmethod
|
||||
def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
if not parameters:
|
||||
parameters = {}
|
||||
|
||||
parameters['oauth_token'] = token.key
|
||||
|
||||
if callback:
|
||||
parameters['oauth_callback'] = escape(callback)
|
||||
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
|
||||
# util function: turn Authorization: header into parameters, has to do some unescaping
|
||||
@staticmethod
|
||||
def _split_header(header):
|
||||
params = {}
|
||||
parts = header.split(',')
|
||||
for param in parts:
|
||||
# ignore realm parameter
|
||||
if param.find('OAuth realm') > -1:
|
||||
continue
|
||||
# remove whitespace
|
||||
param = param.strip()
|
||||
# split key-value
|
||||
param_parts = param.split('=', 1)
|
||||
# remove quotes and unescape the value
|
||||
params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
|
||||
return params
|
||||
|
||||
# util function: turn url string into parameters, has to do some unescaping
|
||||
@staticmethod
|
||||
def _split_url_string(param_str):
|
||||
parameters = cgi.parse_qs(param_str, keep_blank_values=False)
|
||||
for k, v in parameters.iteritems():
|
||||
parameters[k] = urllib.unquote(v[0])
|
||||
return parameters
|
||||
|
||||
# OAuthServer is a worker to check a requests validity against a data store
|
||||
class OAuthServer(object):
|
||||
timestamp_threshold = 300 # in seconds, five minutes
|
||||
version = VERSION
|
||||
signature_methods = None
|
||||
data_store = None
|
||||
|
||||
def __init__(self, data_store=None, signature_methods=None):
|
||||
self.data_store = data_store
|
||||
self.signature_methods = signature_methods or {}
|
||||
|
||||
def set_data_store(self, oauth_data_store):
|
||||
self.data_store = data_store
|
||||
|
||||
def get_data_store(self):
|
||||
return self.data_store
|
||||
|
||||
def add_signature_method(self, signature_method):
|
||||
self.signature_methods[signature_method.get_name()] = signature_method
|
||||
return self.signature_methods
|
||||
|
||||
# process a request_token request
|
||||
# returns the request token on success
|
||||
def fetch_request_token(self, oauth_request):
|
||||
try:
|
||||
# get the request token for authorization
|
||||
token = self._get_token(oauth_request, 'request')
|
||||
except:
|
||||
# no token required for the initial token request
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
self._check_signature(oauth_request, consumer, None)
|
||||
# fetch a new token
|
||||
token = self.data_store.fetch_request_token(consumer)
|
||||
return token
|
||||
|
||||
# process an access_token request
|
||||
# returns the access token on success
|
||||
def fetch_access_token(self, oauth_request):
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
# get the request token
|
||||
token = self._get_token(oauth_request, 'request')
|
||||
self._check_signature(oauth_request, consumer, token)
|
||||
new_token = self.data_store.fetch_access_token(consumer, token)
|
||||
return new_token
|
||||
|
||||
# verify an api call, checks all the parameters
|
||||
def verify_request(self, oauth_request):
|
||||
# -> consumer and token
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
# get the access token
|
||||
token = self._get_token(oauth_request, 'access')
|
||||
self._check_signature(oauth_request, consumer, token)
|
||||
parameters = oauth_request.get_nonoauth_parameters()
|
||||
return consumer, token, parameters
|
||||
|
||||
# authorize a request token
|
||||
def authorize_token(self, token):
|
||||
return self.data_store.authorize_request_token(token)
|
||||
|
||||
# get the callback url
|
||||
def get_callback(self, oauth_request):
|
||||
return oauth_request.get_parameter('oauth_callback')
|
||||
|
||||
# optional support for the authenticate header
|
||||
def build_authenticate_header(self, realm=''):
|
||||
return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
|
||||
|
||||
# verify the correct version request for this server
|
||||
def _get_version(self, oauth_request):
|
||||
try:
|
||||
version = oauth_request.get_parameter('oauth_version')
|
||||
except:
|
||||
version = VERSION
|
||||
if version and version != self.version:
|
||||
raise OAuthError('OAuth version %s not supported' % str(version))
|
||||
return version
|
||||
|
||||
# figure out the signature with some defaults
|
||||
def _get_signature_method(self, oauth_request):
|
||||
try:
|
||||
signature_method = oauth_request.get_parameter('oauth_signature_method')
|
||||
except:
|
||||
signature_method = SIGNATURE_METHOD
|
||||
try:
|
||||
# get the signature method object
|
||||
signature_method = self.signature_methods[signature_method]
|
||||
except:
|
||||
signature_method_names = ', '.join(self.signature_methods.keys())
|
||||
raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names))
|
||||
|
||||
return signature_method
|
||||
|
||||
def _get_consumer(self, oauth_request):
|
||||
consumer_key = oauth_request.get_parameter('oauth_consumer_key')
|
||||
if not consumer_key:
|
||||
raise OAuthError('Invalid consumer key')
|
||||
consumer = self.data_store.lookup_consumer(consumer_key)
|
||||
if not consumer:
|
||||
raise OAuthError('Invalid consumer')
|
||||
return consumer
|
||||
|
||||
# try to find the token for the provided request token key
|
||||
def _get_token(self, oauth_request, token_type='access'):
|
||||
token_field = oauth_request.get_parameter('oauth_token')
|
||||
token = self.data_store.lookup_token(token_type, token_field)
|
||||
if not token:
|
||||
raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
|
||||
return token
|
||||
|
||||
def _check_signature(self, oauth_request, consumer, token):
|
||||
timestamp, nonce = oauth_request._get_timestamp_nonce()
|
||||
self._check_timestamp(timestamp)
|
||||
self._check_nonce(consumer, token, nonce)
|
||||
signature_method = self._get_signature_method(oauth_request)
|
||||
try:
|
||||
signature = oauth_request.get_parameter('oauth_signature')
|
||||
except:
|
||||
raise OAuthError('Missing signature')
|
||||
# attempt to construct the same signature
|
||||
built = signature_method.build_signature(oauth_request, consumer, token)
|
||||
if signature != built:
|
||||
raise OAuthError('Invalid signature')
|
||||
|
||||
def _check_timestamp(self, timestamp):
|
||||
# verify that timestamp is recentish
|
||||
timestamp = int(timestamp)
|
||||
now = int(time.time())
|
||||
lapsed = now - timestamp
|
||||
if lapsed > self.timestamp_threshold:
|
||||
raise OAuthError('Expired timestamp: given %d and now %s has a greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold))
|
||||
|
||||
def _check_nonce(self, consumer, token, nonce):
|
||||
# verify that the nonce is uniqueish
|
||||
try:
|
||||
self.data_store.lookup_nonce(consumer, token, nonce)
|
||||
raise OAuthError('Nonce already used: %s' % str(nonce))
|
||||
except:
|
||||
pass
|
||||
|
||||
# OAuthClient is a worker to attempt to execute a request
|
||||
class OAuthClient(object):
|
||||
consumer = None
|
||||
token = None
|
||||
|
||||
def __init__(self, oauth_consumer, oauth_token):
|
||||
self.consumer = oauth_consumer
|
||||
self.token = oauth_token
|
||||
|
||||
def get_consumer(self):
|
||||
return self.consumer
|
||||
|
||||
def get_token(self):
|
||||
return self.token
|
||||
|
||||
def fetch_request_token(self, oauth_request):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_access_token(self, oauth_request):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def access_resource(self, oauth_request):
|
||||
# -> some protected resource
|
||||
raise NotImplementedError
|
||||
|
||||
# OAuthDataStore is a database abstraction used to lookup consumers and tokens
|
||||
class OAuthDataStore(object):
|
||||
|
||||
def lookup_consumer(self, key):
|
||||
# -> OAuthConsumer
|
||||
raise NotImplementedError
|
||||
|
||||
def lookup_token(self, oauth_consumer, token_type, token_token):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def lookup_nonce(self, oauth_consumer, oauth_token, nonce, timestamp):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_request_token(self, oauth_consumer):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_access_token(self, oauth_consumer, oauth_token):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def authorize_request_token(self, oauth_token):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
# OAuthSignatureMethod is a strategy class that implements a signature method
|
||||
class OAuthSignatureMethod(object):
|
||||
def get_name():
|
||||
# -> str
|
||||
raise NotImplementedError
|
||||
|
||||
def build_signature(oauth_request, oauth_consumer, oauth_token):
|
||||
# -> str
|
||||
raise NotImplementedError
|
||||
|
||||
class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
|
||||
|
||||
def get_name(self):
|
||||
return 'HMAC-SHA1'
|
||||
|
||||
def build_signature(self, oauth_request, consumer, token):
|
||||
sig = (
|
||||
escape(oauth_request.get_normalized_http_method()),
|
||||
escape(oauth_request.get_normalized_http_url()),
|
||||
escape(oauth_request.get_normalized_parameters()),
|
||||
)
|
||||
|
||||
key = '%s&' % consumer.secret
|
||||
if token:
|
||||
key += token.secret
|
||||
raw = '&'.join(sig)
|
||||
|
||||
# hmac object
|
||||
hashed = hmac.new(key, raw, hashlib.sha1)
|
||||
|
||||
# calculate the digest base 64
|
||||
return base64.b64encode(hashed.digest())
|
||||
|
||||
class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
|
||||
|
||||
def get_name(self):
|
||||
return 'PLAINTEXT'
|
||||
|
||||
def build_signature(self, oauth_request, consumer, token):
|
||||
# concatenate the consumer key and secret
|
||||
sig = escape(consumer.secret)
|
||||
if token:
|
||||
sig = '&'.join((sig, escape(token.secret)))
|
||||
return sig
|
Loading…
Add table
Add a link
Reference in a new issue