Skip to content

Commit

Permalink
devilry_examiner|devilry_rest: Authentication.
Browse files Browse the repository at this point in the history
  • Loading branch information
espenak committed Jun 12, 2013
1 parent 1c400e8 commit 9c7e7a5
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from simple_rest import Resource
from simple_rest.response import RESTfulResponse
from devilry_rest.auth import authentication_required




@authentication_required
class AssignmentListing(Resource):
@RESTfulResponse()
def get(self, request, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion src/devilry_examiner/devilry_examiner/rest/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .assignmentlisting import AssignmentListing

urlpatterns = patterns('',
url(r'^assignmentlisting(\.(?P<_format>[a-zA-Z]+))?/?$', AssignmentListing.as_view(),
url(r'^assignmentlisting(\.(?P<_format>[a-zA-Z]+))?/?$',
AssignmentListing.as_view(),
name='devilry_examiner-rest-assignmentlisting'),
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ def setUp(self):
#'duck4000:admin(adminone,admintwo,singleadmin)'])
self.client = RestClient()

def _listas(self, username, **data):
self.client.login(username=username, password='test')

def _get(self, **data):
return self.client.rest_get(reverse('devilry_examiner-rest-assignmentlisting'), **data)

def test_list(self):
def _getas(self, username, **data):
self.client.login(username=username, password='test')
return self._get(**data)

def test_get(self):
self.testhelper.create_user('examiner1')
content, response = self._listas('examiner1')
content, response = self._getas('examiner1')
self.assertEquals(response.status_code, 200)
self.assertEquals(content, ['Hello', 'world'])

def test_get_noauth(self):
content, response = self._get()
self.assertEquals(response.status_code, 401)
96 changes: 96 additions & 0 deletions src/devilry_rest/devilry_rest/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import hmac
import hashlib
from datetime import datetime
from simple_rest.auth.decorators import request_passes_test
from simple_rest.utils.decorators import wrap_object




#def get_secret_key(request, *args, **kwargs):
# TODO:
# return request.user.secret_key
#return 'test'


def authentication_required(obj):
"""
Requires that the user be authenticated either by a signature or by
being actively logged in.
"""
def test_func(request, *args, **kwargs):
#secret_key = get_secret_key(request, *args, **kwargs)
#return validate_signature(request, secret_key) or request.user.is_authenticated()
return request.user.is_authenticated()

decorator = request_passes_test(test_func)
return wrap_object(obj, decorator)



def calculate_signature(key, data, timestamp):
"""
Calculates the signature for the given request data.
"""
# Construct the message from the timestamp and the data in the request
message = str(timestamp) + ''.join("%s%s" % (k,v) for k,v in sorted(data.items()))

# Calculate the signature (HMAC SHA512) according to RFC 2104
signature = hmac.HMAC(str(key), message, hashlib.sha512).hexdigest()

return signature




def validate_signature(request, secret_key):
"""
Validates the signature associated with the given request.
"""

# Extract the request parameters according to the HTTP method
data = request.GET.copy()
if request.method != 'GET':
message_body = getattr(request, request.method, {})
data.update(message_body)

# Make sure the request contains a signature
if data.get('sig', False):
sig = data['sig']
del data['sig']
else:
return False

# Make sure the request contains a timestamp
if data.get('t', False):
timestamp = int(data.get('t', False))
del data['t']
else:
return False

# Make sure the signature has not expired
delta = datetime.utcnow() - datetime.utcfromtimestamp(timestamp)
if delta.seconds > 5 * 60: # If the signature is older than 5 minutes, it's invalid
return False

# Make sure the signature is valid
return sig == calculate_signature(secret_key, data, timestamp)




def signature_required(secret_key_func):
"""
Requires that the request contain a valid signature to gain access
to a specified resource.
"""
def actual_decorator(obj):

def test_func(request, *args, **kwargs):
secret_key = secret_key_func(request, *args, **kwargs)
return validate_signature(request, secret_key)

decorator = request_passes_test(test_func)
return wrap_object(obj, decorator)

return actual_decorator

0 comments on commit 9c7e7a5

Please sign in to comment.