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
195
python_apps/soundcloud-api/scapi/authentication.py
Normal file
195
python_apps/soundcloud-api/scapi/authentication.py
Normal file
|
@ -0,0 +1,195 @@
|
|||
## SouncCloudAPI implements a Python wrapper around the SoundCloud RESTful
|
||||
## API
|
||||
##
|
||||
## Copyright (C) 2008 Diez B. Roggisch
|
||||
## Contact mailto:deets@soundcloud.com
|
||||
##
|
||||
## This library is free software; you can redistribute it and/or
|
||||
## modify it under the terms of the GNU Lesser General Public
|
||||
## License as published by the Free Software Foundation; either
|
||||
## version 2.1 of the License, or (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
## Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public
|
||||
## License along with this library; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
import base64
|
||||
import time, random
|
||||
import urlparse
|
||||
import hmac
|
||||
import hashlib
|
||||
from scapi.util import escape
|
||||
import logging
|
||||
|
||||
|
||||
USE_DOUBLE_ESCAPE_HACK = True
|
||||
"""
|
||||
There seems to be an uncertainty on the way
|
||||
parameters are to be escaped. For now, this
|
||||
variable switches between two escaping mechanisms.
|
||||
|
||||
If True, the passed parameters - GET or POST - are
|
||||
escaped *twice*.
|
||||
"""
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OAuthSignatureMethod_HMAC_SHA1(object):
|
||||
|
||||
FORBIDDEN = ['realm', 'oauth_signature']
|
||||
|
||||
def get_name(self):
|
||||
return 'HMAC-SHA1'
|
||||
|
||||
def build_signature(self, request, parameters, consumer_secret, token_secret, oauth_parameters):
|
||||
if logger.level == logging.DEBUG:
|
||||
logger.debug("request: %r", request)
|
||||
logger.debug("parameters: %r", parameters)
|
||||
logger.debug("consumer_secret: %r", consumer_secret)
|
||||
logger.debug("token_secret: %r", token_secret)
|
||||
logger.debug("oauth_parameters: %r", oauth_parameters)
|
||||
|
||||
|
||||
temp = {}
|
||||
temp.update(oauth_parameters)
|
||||
for p in self.FORBIDDEN:
|
||||
if p in temp:
|
||||
del temp[p]
|
||||
if parameters is not None:
|
||||
temp.update(parameters)
|
||||
sig = (
|
||||
escape(self.get_normalized_http_method(request)),
|
||||
escape(self.get_normalized_http_url(request)),
|
||||
self.get_normalized_parameters(temp), # these are escaped in the method already
|
||||
)
|
||||
|
||||
key = '%s&' % consumer_secret
|
||||
if token_secret is not None:
|
||||
key += token_secret
|
||||
raw = '&'.join(sig)
|
||||
logger.debug("raw basestring: %s", raw)
|
||||
logger.debug("key: %s", key)
|
||||
# hmac object
|
||||
hashed = hmac.new(key, raw, hashlib.sha1)
|
||||
# calculate the digest base 64
|
||||
signature = escape(base64.b64encode(hashed.digest()))
|
||||
return signature
|
||||
|
||||
|
||||
def get_normalized_http_method(self, request):
|
||||
return request.get_method().upper()
|
||||
|
||||
|
||||
# parses the url and rebuilds it to be scheme://host/path
|
||||
def get_normalized_http_url(self, request):
|
||||
url = request.get_full_url()
|
||||
parts = urlparse.urlparse(url)
|
||||
url_string = '%s://%s%s' % (parts.scheme, parts.netloc, parts.path)
|
||||
return url_string
|
||||
|
||||
|
||||
def get_normalized_parameters(self, params):
|
||||
if params is None:
|
||||
params = {}
|
||||
try:
|
||||
# exclude the signature if it exists
|
||||
del params['oauth_signature']
|
||||
except:
|
||||
pass
|
||||
key_values = []
|
||||
|
||||
for key, values in params.iteritems():
|
||||
if isinstance(values, file):
|
||||
continue
|
||||
if isinstance(values, (int, long, float)):
|
||||
values = str(values)
|
||||
if isinstance(values, (list, tuple)):
|
||||
values = [str(v) for v in values]
|
||||
if isinstance(values, basestring):
|
||||
values = [values]
|
||||
if USE_DOUBLE_ESCAPE_HACK and not key.startswith("ouath"):
|
||||
key = escape(key)
|
||||
for v in values:
|
||||
v = v.encode("utf-8")
|
||||
key = key.encode("utf-8")
|
||||
if USE_DOUBLE_ESCAPE_HACK and not key.startswith("oauth"):
|
||||
# this is a dirty hack to make the
|
||||
# thing work with the current server-side
|
||||
# implementation. Or is it by spec?
|
||||
v = escape(v)
|
||||
key_values.append(escape("%s=%s" % (key, v)))
|
||||
# sort lexicographically, first after key, then after value
|
||||
key_values.sort()
|
||||
# combine key value pairs in string
|
||||
return escape('&').join(key_values)
|
||||
|
||||
|
||||
class OAuthAuthenticator(object):
|
||||
OAUTH_API_VERSION = '1.0'
|
||||
AUTHORIZATION_HEADER = "Authorization"
|
||||
|
||||
def __init__(self, consumer=None, consumer_secret=None, token=None, secret=None, signature_method=OAuthSignatureMethod_HMAC_SHA1()):
|
||||
if consumer == None:
|
||||
raise ValueError("The consumer key must be passed for all public requests; it may not be None")
|
||||
self._consumer, self._token, self._secret = consumer, token, secret
|
||||
self._consumer_secret = consumer_secret
|
||||
self._signature_method = signature_method
|
||||
random.seed()
|
||||
|
||||
|
||||
def augment_request(self, req, parameters, use_multipart=False, oauth_callback=None, oauth_verifier=None):
|
||||
oauth_parameters = {
|
||||
'oauth_consumer_key': self._consumer,
|
||||
'oauth_timestamp': self.generate_timestamp(),
|
||||
'oauth_nonce': self.generate_nonce(),
|
||||
'oauth_version': self.OAUTH_API_VERSION,
|
||||
'oauth_signature_method': self._signature_method.get_name(),
|
||||
#'realm' : "http://soundcloud.com",
|
||||
}
|
||||
if self._token is not None:
|
||||
oauth_parameters['oauth_token'] = self._token
|
||||
|
||||
if oauth_callback is not None:
|
||||
oauth_parameters['oauth_callback'] = oauth_callback
|
||||
|
||||
if oauth_verifier is not None:
|
||||
oauth_parameters['oauth_verifier'] = oauth_verifier
|
||||
|
||||
# in case we upload large files, we don't
|
||||
# sign the request over the parameters
|
||||
# There's a bug in the OAuth 1.0 (and a) specs that says that PUT request should omit parameters from the base string.
|
||||
# This is fixed in the IETF draft, don't know when this will be released though. - HT
|
||||
if use_multipart or req.get_method() == 'PUT':
|
||||
parameters = None
|
||||
|
||||
oauth_parameters['oauth_signature'] = self._signature_method.build_signature(req,
|
||||
parameters,
|
||||
self._consumer_secret,
|
||||
self._secret,
|
||||
oauth_parameters)
|
||||
def to_header(d):
|
||||
return ",".join('%s="%s"' % (key, value) for key, value in sorted(oauth_parameters.items()))
|
||||
|
||||
req.add_header(self.AUTHORIZATION_HEADER, "OAuth %s" % to_header(oauth_parameters))
|
||||
|
||||
def generate_timestamp(self):
|
||||
return int(time.time())# * 1000.0)
|
||||
|
||||
def generate_nonce(self, length=8):
|
||||
return ''.join(str(random.randint(0, 9)) for i in range(length))
|
||||
|
||||
|
||||
class BasicAuthenticator(object):
|
||||
|
||||
def __init__(self, user, password, consumer, consumer_secret):
|
||||
self._base64string = base64.encodestring("%s:%s" % (user, password))[:-1]
|
||||
self._x_auth_header = 'OAuth oauth_consumer_key="%s" oauth_consumer_secret="%s"' % (consumer, consumer_secret)
|
||||
|
||||
def augment_request(self, req, parameters):
|
||||
req.add_header("Authorization", "Basic %s" % self._base64string)
|
||||
req.add_header("X-Authorization", self._x_auth_header)
|
Loading…
Add table
Add a link
Reference in a new issue