Skip to content

Commit

Permalink
Merge branch 'hotfix/unify-multi-and-sigle-key'
Browse files Browse the repository at this point in the history
  • Loading branch information
radeklos committed Oct 27, 2015
2 parents 5917f26 + 0fa4a86 commit a703479
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 55 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Change Log
----------

1.3.0
~~~~~
- Merge validation of multiple and single signature to single method
- Replace middleware classes to one single class HmacMiddleware

1.2.0
~~~~~
- Decorators
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.1
1.3.0
2 changes: 1 addition & 1 deletion djangohmac/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def decorator(route):
@wraps(route)
def _wrapped_view(view, request, *args, **kwargs):
try:
shmac.validate_multiple_signatures(request, only)
shmac.validate_signature(request, only)
except HmacException:
shmac.abort()
return route(view, request, *args, **kwargs)
Expand Down
13 changes: 1 addition & 12 deletions djangohmac/middleware.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
from .sign import HmacException, shmac


class GlobalHmacMiddleware(object):
class HmacMiddleware(object):
""" Uses global signature `HMAC_SECRET` defined in settings
"""
def process_request(self, request):
try:
shmac.validate_signature(request)
except HmacException:
shmac.abort()


class MultipleHmacMiddleware(object):
""" Uses multiple signatures defined in `HMAC_SECRETS` in settings
"""

def process_request(self, request):
try:
shmac.validate_multiple_signatures(request)
except HmacException:
shmac.abort()
90 changes: 64 additions & 26 deletions djangohmac/sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ def hmac_key(self):
return six.b(settings.HMAC_SECRET)

def get_signature(self, request):
""" Get signature from djagno requests
Arguments:
request: Django request
Returns:
string: HMAC signature
Raises:
SecretKeyIsNotSet
"""

try:
return request.META['HTTP_{}'.format(self.header.upper())]
except KeyError:
Expand All @@ -78,6 +90,9 @@ def make_hmac_for(self, name, data=''):
Arguments:
name (str): key name from HMAC_SECRETS dict
data (str): HMAC message
Raises:
UnknownKeyName
"""
try:
key = self.hmac_keys[name]
Expand All @@ -88,17 +103,59 @@ def make_hmac_for(self, name, data=''):
))
return token

def validate_signature(self, request):
def _parse_signature(self, signature):
""" Split signature to user name and signature
Arguments:
signature (string): Signature genrated by `make_hmac_for`
Returns:
tuple of service name and signature
"""
try:
return decode_string(
base64.b64decode(decode_string(signature))
).split(':')
except (TypeError, binascii.Error):
raise InvalidSignature()

def validate_signature(self, request, only=None):
""" Validate signate in given request.
Arguments:
request: Django request
only: list of keys from HMAC_SECRETS to restrict signatures
Returns:
boolean: True when signature is valid otherwice False
Raises:
InvalidSignature
SecretKeyIsNotSet
"""
if self.hmac_disarm:
return True
try:
signature = self.get_signature(request)
key_name, hmac_token_client = self._parse_signature(signature)
if only and key_name not in only:
raise InvalidSignature('This view is only for {}'.format(only))
return self.validate_multiple_signatures(key_name, signature, request)
except ValueError:
return self.validate_single_signature(request)

def validate_single_signature(self, request):
""" Validate signature from djagno request
Arguments:
request (request): Django request class
Returns:
boolen
Raises:
InvalidSignature
"""
if self.hmac_disarm:
return True
hmac_token_client = self.get_signature(request)
hmac_token_server = self.make_hmac(request.body)
if hmac_token_client != hmac_token_server:
Expand All @@ -107,23 +164,7 @@ def validate_signature(self, request):
))
return True

def _parse_multiple_signature(self, signature):
""" Split signature to user name and signature
Arguments:
signature (string): Signature genrated by `make_hmac_for`
Returns:
tuple of service name and signature
"""
try:
return decode_string(
base64.b64decode(decode_string(signature))
).split(':')
except (TypeError, binascii.Error):
raise InvalidSignature()

def validate_multiple_signatures(self, request, only=None):
def validate_multiple_signatures(self, key_name, signature, request):
""" Validate signature from djagno request. But it takes key from
`HMAC_SECRETS` list
Expand All @@ -133,13 +174,10 @@ def validate_multiple_signatures(self, request, only=None):
Returns:
boolen
Raises:
InvalidSignature
"""
if self.hmac_disarm:
return True
signature = self.get_signature(request)
key_name, hmac_token_client = self._parse_multiple_signature(signature)
if only and key_name not in only:
raise InvalidSignature('This view is only for {}'.format(only))
hmac_token_server = self.make_hmac_for(key_name, request.body)
if signature != hmac_token_server:
raise InvalidSignature('Signatures are different: {0} {1}'.format(
Expand Down
4 changes: 1 addition & 3 deletions docs/instalation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ To secure all your app with HMAC you can use a middleware.
MIDDLEWARE_CLASSES = (
# ...
'djangohmac.middleware.GlobalHmacMiddleware',
'djangohmac.middleware.HmacMiddleware',
)
If your application is called by multiple services and each has different secret keys you should use `djangohmac.middleware.GlobalHmacMiddleware` instead.

.. note:: Middleware is applied on all views except the admin!


Expand Down
11 changes: 3 additions & 8 deletions tests/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ def test_custom_secret_key(self):
def test_valid_multiple_signatures_should_pass(self):
signature = self.hmac.make_hmac_for('serviceA')
request = self.factory.get('/example', **{self.header: signature})
assert self.hmac.validate_multiple_signatures(request)
assert self.hmac.validate_signature(request)

def test_invalid_multiple_signatures_should_raice_exception(self):
signature = self.hmac.make_hmac_for('serviceA', 'some data')
request = self.factory.get('/example', **{self.header: signature})
with self.assertRaises(InvalidSignature):
self.hmac.validate_multiple_signatures(request)
self.hmac.validate_signature(request)

def test_unknown_key_name_for_multiple_signatures_should_raice_exception(self):
with self.assertRaises(UnknownKeyName):
Expand All @@ -42,12 +42,7 @@ def test_valid_signature_for_services_are_restricted_view(self):
signature = self.hmac.make_hmac_for('serviceB')
request = self.factory.get('/example', **{self.header: signature})
with self.assertRaises(InvalidSignature):
self.hmac.validate_multiple_signatures(
self.hmac.validate_signature(
request,
only=['serviceA']
)

def test_valid_signature_for_restricted_view(self):
signature = self.hmac.make_hmac_for('serviceA')
request = self.factory.get('/example', **{self.header: signature})
self.hmac.validate_multiple_signatures(request, only=['serviceA'])
8 changes: 4 additions & 4 deletions tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from django.test import RequestFactory, TestCase

# First Party Libs
from djangohmac.middleware import GlobalHmacMiddleware, MultipleHmacMiddleware
from djangohmac.middleware import HmacMiddleware
from djangohmac.sign import Hmac


class GlobalHmacMiddlewareTestCase(TestCase):
class SingleHmacMiddlewareTestCase(TestCase):

def setUp(self):
self.hmacmiddleware = GlobalHmacMiddleware()
self.hmacmiddleware = HmacMiddleware()
self.factory = RequestFactory()
self.hmac = Hmac()

Expand All @@ -34,7 +34,7 @@ def test_raise_exception_when_invalid_hmac_is_send(self):
class MultipleHmacMiddlewareTestCase(TestCase):

def setUp(self):
self.hmacmiddleware = MultipleHmacMiddleware()
self.hmacmiddleware = HmacMiddleware()
self.factory = RequestFactory()
self.hmac = Hmac()

Expand Down

0 comments on commit a703479

Please sign in to comment.