Commits

Jesper Nøhr  committed 76c0ea5

piston does dallas^Hoauth, issue handler makeover, more stuff I forgot

  • Participants
  • Parent commits 9997709

Comments (0)

Files changed (6)

File piston/authentication.py

 from django.http import HttpResponse
 from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required
+from django.conf import settings
+
+import oauth
 
 def django_auth(username, password):
     try:
         resp['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
         resp.status_code = 401
         return resp
+
+
+from store import DataStore
+
+OAUTH_REALM_KEY_NAME = 'OAUTH_REALM_KEY_NAME'
+
+def initialize_server_request(request):
+    """Shortcut for initialization."""
+    oauth_request = oauth.OAuthRequest.from_request(request.method, 
+                                                    request.build_absolute_uri(), 
+                                                    headers=request.META,
+                                                    parameters=dict(request.REQUEST.items()),
+                                                    query_string=request.environ.get('QUERY_STRING', ''))
+    if oauth_request:
+        oauth_server = oauth.OAuthServer(DataStore(oauth_request))
+        oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT())
+        oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
+    else:
+        oauth_server = None
+        
+    return oauth_server, oauth_request
+
+def send_oauth_error(err=None):
+    """Shortcut for sending an error."""
+    # send a 401 error
+    response = HttpResponse(err.message.encode('utf-8'))
+    response.status_code = 401
+    # return the authenticate header
+    realm = getattr(settings, OAUTH_REALM_KEY_NAME, 'Realm something')
+    header = oauth.build_authenticate_header(realm=realm)
+    for k, v in header.iteritems():
+        response[k] = v
+    return response
+
+def oauth_request_token(request):
+    oauth_server, oauth_request = initialize_server_request(request)
+    
+    if oauth_server is None:
+        return INVALID_PARAMS_RESPONSE
+    try:
+        # create a request token
+        token = oauth_server.fetch_request_token(oauth_request)
+        # return the token
+        response = HttpResponse(token.to_string())
+    except oauth.OAuthError, err:
+        response = send_oauth_error(err)
+    return response
+
+def oauth_auth_view(request, token, callback, params):
+    return HttpResponse("Just a fake view for auth.")
+
+@login_required
+def oauth_user_auth(request):
+    oauth_server, oauth_request = initialize_server_request(request)
+    
+    if oauth_request is None:
+        return INVALID_PARAMS_RESPONSE
+        
+    try:
+        token = oauth_server.fetch_request_token(oauth_request)
+    except oath.OAuthError, err:
+        return send_oauth_error(err)
+        
+    try:
+        callback = oauth_server.get_callback(oauth_request)
+    except:
+        callback = None
+        
+    if request.method == "GET":
+        request.session['oauth'] = token.key
+        params = oauth_request.get_normalized_parameters()
+        return oauth_auth_view(request, token, callback, params)
+    elif request.method == "POST":
+        if request.session.get('oauth', '') == token.key:
+            request.session['oauth'] = ''
+            
+            try:
+                if int(request.POST.get('authorize_access')):
+                    token = oauth_server.authorize_token(token, request.user)
+                    args = token.to_string(only_key=True)
+                else:
+                    args = 'error=%s' % 'Access not granted by user.'
+                
+                response = HttpResponse('Fake callback.')
+                    
+            except OAuthError, err:
+                response = send_oauth_error(err)
+        else:
+            response = HttpResponse('Action not allowed.')
+            
+        return response
+
+def oauth_access_token(request):
+    oauth_server, oauth_request = initialize_server_request(request)
+    
+    if oauth_request is None:
+        return INVALID_PARAMS_RESPONSE
+        
+    try:
+        token = oauth_server.fetch_access_token(oauth_request)
+        return HttpResponse(token.to_string())
+    except oauth.OAuthError, err:
+        return send_oauth_error(err)
+
+def oauth_protected_area(request):
+    oauth_server, oauth_request = initialize_server_request(request)
+
+    try:
+        consumer, token, parameters = oauth_server.verify_request(oauth_request)
+        return HttpResponse("protected resource, consumer=%s, token=%s, parameters=%s, you are=%s" % (consumer, token, parameters, token.user))
+    except oauth.OAuthError, err:
+        return send_oauth_error(err)
+
+    return HttpResponse("OK = %s" % ok)
+                
+INVALID_PARAMS_RESPONSE = send_oauth_error(oauth.OAuthError('Invalid request parameters.'))
+                
+class OAuthAuthentication(object):
+    """
+    OAuth authentication. Based on work by Leah Culver.
+    """
+    def __init__(self, realm='Bitbucket.org HTTP'):
+        self.realm = realm
+    
+    def is_authenticated(self, request):
+        oauth_server, oauth_request = initialize_server_request(request)
+
+        if oauth_request is None:
+            return False
+
+        token = oauth_server.fetch_request_token(oauth_request)
+        callback = oauth_server.get_callback(oauth_request)
+        
+        print token
+        print token.to_string()
+        print callback
+        
+        return False
+        
+    def challenge(self):
+        return INVALID_PARAMS_RESPONSE
+        
+    
+        
         

File piston/emitters.py

                 try: ret['resource_uri'] = data.get_api_url()
                 except: pass
             
+            # absolute uri
+            if hasattr(data, 'get_absolute_url'):
+                try: ret['absolute_uri'] = data.get_absolute_url()
+                except: pass
+            
             return ret
         
         def _list(data):

File piston/managers.py

+from django.db import models
+from django.contrib.auth.models import User
+
+KEY_SIZE = 16
+SECRET_SIZE = 16
+
+class ConsumerManager(models.Manager):
+    def create_consumer(self, name, user=None):
+        """Shortcut to create a consumer with random key/secret."""
+        consumer, created = self.get_or_create(name=name)
+        if user is not None:
+            consumer.user = user
+        if created:
+            consumer.generate_random_codes()
+        return consumer
+    
+    _default_consumer = None
+    def get_default_consumer(self, name):
+        """Add cache if you use a default consumer."""
+        if self._default_consumer is None:
+            self._default_consumer = self.get(name=name)
+        return self._default_consumer
+        
+
+class ResourceManager(models.Manager):
+    _default_resource = None
+    def get_default_resource(self, name):
+        """Add cache if you use a default resource."""
+        if self._default_resource is None:
+            self._default_resource = self.get(name=name)
+        return self._default_resource
+        
+
+class TokenManager(models.Manager):
+    def create_token(self, consumer, token_type, timestamp, user=None):
+        """Shortcut to create a token with random key/secret."""
+        token, created = self.get_or_create(consumer=consumer, 
+                                            token_type=token_type, 
+                                            timestamp=timestamp,
+                                            user=user)
+        if created:
+            token.generate_random_codes()
+        return token

File piston/models.py

+import urllib
+from django.db import models
+from django.contrib.auth.models import User
+
+from managers import TokenManager, ConsumerManager, ResourceManager
+
+KEY_SIZE = 16
+SECRET_SIZE = 16
+
+class Nonce(models.Model):
+    token_key = models.CharField(max_length=KEY_SIZE)
+    consumer_key = models.CharField(max_length=KEY_SIZE)
+    key = models.CharField(max_length=255)
+    
+    def __unicode__(self):
+        return u"Nonce %s for %s" % (self.key, self.consumer_key)
+
+
+class Resource(models.Model):
+    name = models.CharField(max_length=255)
+    url = models.TextField(max_length=2047)
+    is_readonly = models.BooleanField(default=True)
+    
+    objects = ResourceManager()
+
+    def __unicode__(self):
+        return u"Resource %s with url %s" % (self.name, self.url)
+
+
+class Consumer(models.Model):
+    name = models.CharField(max_length=255)
+    key = models.CharField(max_length=KEY_SIZE)
+    secret = models.CharField(max_length=SECRET_SIZE)
+    
+    user = models.ForeignKey(User, null=True, blank=True)
+
+    objects = ConsumerManager()
+        
+    def __unicode__(self):
+        return u"Consumer %s with key %s" % (self.name, self.key)
+
+    def generate_random_codes(self):
+        key = User.objects.make_random_password(length=KEY_SIZE)
+
+        secret = User.objects.make_random_password(length=SECRET_SIZE)
+        while Consumer.objects.filter(key__exact=key, secret__exact=secret).count():
+            secret = User.objects.make_random_password(length=SECRET_SIZE)
+
+        self.key = key
+        self.secret = secret
+        self.save()
+
+
+class Token(models.Model):
+    REQUEST = 1
+    ACCESS = 2
+    TOKEN_TYPES = ((REQUEST, u'Request'), (ACCESS, u'Access'))
+    
+    key = models.CharField(max_length=KEY_SIZE)
+    secret = models.CharField(max_length=SECRET_SIZE)
+    token_type = models.IntegerField(choices=TOKEN_TYPES)
+    timestamp = models.IntegerField()
+    is_approved = models.BooleanField(default=False)
+    
+    user = models.ForeignKey(User, null=True, blank=True)
+    consumer = models.ForeignKey(Consumer)
+#    resource = models.ForeignKey(Resource)
+    
+    objects = TokenManager()
+    
+    def __unicode__(self):
+        return u"%s Token %s for %s" % (self.get_token_type_display(), self.key, self.consumer)
+
+    def to_string(self, only_key=False):
+        token_dict = {
+            'oauth_token': self.key, 
+            'oauth_token_secret': self.secret
+        }
+        if only_key:
+            del token_dict['oauth_token_secret']
+        return urllib.urlencode(token_dict)
+
+    def generate_random_codes(self):
+        key = User.objects.make_random_password(length=KEY_SIZE)
+        secret = User.objects.make_random_password(length=SECRET_SIZE)
+
+        while Token.objects.filter(key__exact=key, secret__exact=secret).count():
+            secret = User.objects.make_random_password(length=SECRET_SIZE)
+
+        self.key = key
+        self.secret = secret
+        self.save()

File piston/oauth.py

+import cgi
+import urllib
+import time
+import random
+import urlparse
+import hmac
+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 += ', %s="%s"' % (k, escape(str(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' % (escape(str(k)), escape(str(v))) for k, v 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[0], parts[1], parts[2]) # scheme, netloc, 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, parameters=None, query_string=None):
+        # combine multiple parameter sources
+        if parameters is None:
+            parameters = {}
+
+        # headers
+        if headers and 'Authorization' in headers:
+            auth_header = headers['Authorization']
+            # check that the authorization header is OAuth
+            if auth_header.index('OAuth') > -1:
+                try:
+                    # get the parameters from the header
+                    header_params = OAuthRequest._split_header(auth_header)
+                    parameters.update(header_params)
+                except:
+                    raise OAuthError('Unable to parse OAuth parameters from Authorization header.')
+
+        # GET or POST query string
+        if query_string:
+            query_params = OAuthRequest._split_url_string(query_string)
+            parameters.update(query_params)
+
+        # URL parameters
+        param_str = urlparse.urlparse(http_url)[4] # query
+        url_params = OAuthRequest._split_url_string(param_str)
+        parameters.update(url_params)
+
+        if parameters:
+            return OAuthRequest(http_method, http_url, parameters)
+
+        return None
+
+    @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 OAuthError:
+            # 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, user):
+        return self.data_store.authorize_request_token(token, user)
+    
+    # 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.')
+        # validate the signature
+        valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature)
+        if not valid_sig:
+            key, base = signature_method.build_signature_base_string(oauth_request, consumer, token)
+            raise OAuthError('Invalid signature. Expected signature base string: %s' % base)
+        built = signature_method.build_signature(oauth_request, consumer, token)
+
+    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
+        nonce = self.data_store.lookup_nonce(consumer, token, nonce)
+        if nonce:
+            raise OAuthError('Nonce already used: %s' % str(nonce))
+
+# 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, user):
+        # -> OAuthToken
+        raise NotImplementedError
+
+# OAuthSignatureMethod is a strategy class that implements a signature method
+class OAuthSignatureMethod(object):
+    def get_name(self):
+        # -> str
+        raise NotImplementedError
+
+    def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
+        # -> str key, str raw
+        raise NotImplementedError
+
+    def build_signature(self, oauth_request, oauth_consumer, oauth_token):
+        # -> str
+        raise NotImplementedError
+
+    def check_signature(self, oauth_request, consumer, token, signature):
+        built = self.build_signature(oauth_request, consumer, token)
+        return built == signature
+
+class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
+
+    def get_name(self):
+        return 'HMAC-SHA1'
+        
+    def build_signature_base_string(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&' % escape(consumer.secret)
+        if token:
+            key += escape(token.secret)
+        raw = '&'.join(sig)
+        return key, raw
+
+    def build_signature(self, oauth_request, consumer, token):
+        # build the base signature string
+        key, raw = self.build_signature_base_string(oauth_request, consumer, token)
+
+        # hmac object
+        try:
+            import hashlib # 2.5
+            hashed = hmac.new(key, raw, hashlib.sha1)
+        except:
+            import sha # deprecated
+            hashed = hmac.new(key, raw, sha)
+
+        # calculate the digest base 64
+        return base64.b64encode(hashed.digest())
+
+class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
+
+    def get_name(self):
+        return 'PLAINTEXT'
+
+    def build_signature_base_string(self, oauth_request, consumer, token):
+        # concatenate the consumer key and secret
+        sig = escape(consumer.secret) + '&'
+        if token:
+            sig = sig + escape(token.secret)
+        return sig
+
+    def build_signature(self, oauth_request, consumer, token):
+        return self.build_signature_base_string(oauth_request, consumer, token)

File piston/store.py

+import oauth
+
+from models import Nonce, Token, Consumer
+
+class DataStore(oauth.OAuthDataStore):
+    """Layer between Python OAuth and Django database."""
+    def __init__(self, oauth_request):
+        self.signature = oauth_request.parameters.get('oauth_signature', None)
+        self.timestamp = oauth_request.parameters.get('oauth_timestamp', None)
+        self.scope = oauth_request.parameters.get('scope', 'all')
+
+    def lookup_consumer(self, key):
+        try:
+            self.consumer = Consumer.objects.get(key=key)
+            return self.consumer
+        except Consumer.DoesNotExist:
+            return None
+
+    def lookup_token(self, token_type, token):
+        if token_type == 'request':
+            token_type = Token.REQUEST
+        elif token_type == 'access':
+            token_type = Token.ACCESS
+        try:
+            self.request_token = Token.objects.get(key=token, 
+                                                   token_type=token_type)
+            return self.request_token
+        except Token.DoesNotExist:
+            return None
+
+    def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
+        if oauth_token is None:
+            return None
+        nonce, created = Nonce.objects.get_or_create(consumer_key=oauth_consumer.key, 
+                                                     token_key=oauth_token.key,
+                                                     key=nonce)
+        if created:
+            return None
+        else:
+            return nonce.key
+
+    def fetch_request_token(self, oauth_consumer):
+        if oauth_consumer.key == self.consumer.key:
+            self.request_token = Token.objects.create_token(consumer=self.consumer,
+                                                            token_type=Token.REQUEST,
+                                                            timestamp=self.timestamp)
+            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 \
+        and self.request_token.is_approved:
+            self.access_token = Token.objects.create_token(consumer=self.consumer,
+                                                           token_type=Token.ACCESS,
+                                                           timestamp=self.timestamp,
+                                                           user=self.request_token.user)
+            return self.access_token
+        return None
+
+    def authorize_request_token(self, oauth_token, user):
+        if oauth_token.key == self.request_token.key:
+            # authorize the request token in the store
+            self.request_token.is_approved = True
+            self.request_token.user = user
+            self.request_token.save()
+            return self.request_token
+        return None