From 42e83727ddf7879ceceeb50bb9bd95a1aaef3f1d Mon Sep 17 00:00:00 2001 From: Nikolai Kristiansen Date: Thu, 17 Oct 2013 14:51:44 +0200 Subject: [PATCH] Nikolais ninja workbranch --- dusken/api/member.py | 2 +- dusken/authentication.py | 19 +++++++++++++++++++ dusken/fixtures/member.json | 20 +++++++++++++++++++- dusken/models.py | 24 ++++++++++++++++++++++-- dusken/settings-sample.py | 1 + dusken/tests/member.py | 10 ++++++---- dusken/tests/membership.py | 15 +++++++++------ dusken/tests/support/test.py | 2 +- requirements.txt | 15 +++++++-------- utils/tests.py | 2 +- 10 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 dusken/authentication.py diff --git a/dusken/api/member.py b/dusken/api/member.py index 787886e..1242856 100644 --- a/dusken/api/member.py +++ b/dusken/api/member.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import User from django.conf.urls import url from django.shortcuts import get_object_or_404 from tastypie import fields -from tastypie.authentication import ApiKeyAuthentication +from dusken.authentication import ApiKeyAuthentication from tastypie.authorization import DjangoAuthorization from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpForbidden, HttpNoContent, HttpResponse, HttpAccepted diff --git a/dusken/authentication.py b/dusken/authentication.py new file mode 100644 index 0000000..df57e7c --- /dev/null +++ b/dusken/authentication.py @@ -0,0 +1,19 @@ +from tastypie.authentication import ApiKeyAuthentication as tastypieApiKeyAuth + +class ApiKeyAuthentication(tastypieApiKeyAuth): + """ + Override with our own ApiKey model. + """ + def get_key(self, user, api_key): + """ + Attempts to find the API key for the user. Uses ``ApiKey`` by default + but can be overridden. + """ + from dusken.models import ApiKey + + try: + ApiKey.objects.get(user=user, key=api_key) + except ApiKey.DoesNotExist: + return self._unauthorized() + + return True diff --git a/dusken/fixtures/member.json b/dusken/fixtures/member.json index 872fbb1..e665b9d 100644 --- a/dusken/fixtures/member.json +++ b/dusken/fixtures/member.json @@ -1,4 +1,22 @@ [ + { + "pk": 2, + "model": "auth.user", + "fields": { + "username": "", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "last_login": "2013-10-17T10:59:18.493Z", + "groups": [], + "user_permissions": [], + "password": "", + "email": "", + "date_joined": "2013-10-17T10:59:18.493Z" + } + }, { "pk": 2, "model": "dusken.member", @@ -14,4 +32,4 @@ "user_permissions": [] } } -] \ No newline at end of file +] diff --git a/dusken/models.py b/dusken/models.py index 20d9acd..1a87ab9 100644 --- a/dusken/models.py +++ b/dusken/models.py @@ -1,6 +1,16 @@ import django from django.db import models -from tastypie.models import create_api_key +import tastypie +from tastypie.models import ApiKey as tastypieApiKey +import uuid +import hmac + +# TODO override ApiKeyAuthentication +try: + from hashlib import sha1 +except ImportError: + import sha + sha1 = sha.sha class BaseModel(models.Model): created = models.DateTimeField(auto_now_add=True) @@ -9,8 +19,18 @@ class BaseModel(models.Model): class Meta: abstract = True + +class ApiKey(tastypieApiKey): + user = models.OneToOneField('dusken.Member', related_name='api_key') -class Member(django.contrib.auth.models.User): +def create_api_key(sender, **kwargs): + """ + A signal for hooking up automatic ``ApiKey`` creation. + """ + if kwargs.get('created') is True: + ApiKey.objects.create(user=kwargs.get('instance')) + +class Member(django.contrib.auth.models.AbstractUser): def __unicode__(self): if len(self.first_name) + len(self.last_name) > 0: return u'{first} {last} ({username})'.format( diff --git a/dusken/settings-sample.py b/dusken/settings-sample.py index 895d3e6..08dcbeb 100644 --- a/dusken/settings-sample.py +++ b/dusken/settings-sample.py @@ -155,3 +155,4 @@ LOGGING = { } TASTYPIE_DEFAULT_FORMATS = ['json'] +TASTYPIE_ABSTRACT_APIKEY = True diff --git a/dusken/tests/member.py b/dusken/tests/member.py index a6b47b1..76b495f 100644 --- a/dusken/tests/member.py +++ b/dusken/tests/member.py @@ -1,9 +1,8 @@ import logging -from dusken.models import Address, Country, Member +from dusken.models import Address, Country, Member, ApiKey from support.test import ResourceTestCase -from tastypie.models import ApiKey -from utils.tests import test_fixtures +from utils.tests import test_fixtures_member class MemberTestBase(ResourceTestCase): @@ -193,7 +192,8 @@ class MemberTest(MemberTestBase): """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" class MemberAddressTest(MemberTestBase): - fixtures = test_fixtures() + # Ref: https://docs.djangoproject.com/en/dev/topics/testing/overview/#django.test.TransactionTestCase.fixtures + fixtures = test_fixtures_member() def setUp(self): super(MemberAddressTest, self).setUp() @@ -233,6 +233,8 @@ class MemberAddressTest(MemberTestBase): data=data, authentication=self.creds) + import pdb + pdb.set_trace() self.assertHttpAccepted(response) self.assertEquals('Amsterdam', Member.objects.get(id=self.user_id).address.city) diff --git a/dusken/tests/membership.py b/dusken/tests/membership.py index e2db7b6..3bc62d7 100644 --- a/dusken/tests/membership.py +++ b/dusken/tests/membership.py @@ -1,21 +1,24 @@ import logging +import django -from dusken.models import Address, Country, Member +from dusken.models import Address, Country, Member, ApiKey from support.test import ResourceTestCase -from tastypie.models import ApiKey -from utils.tests import load_test_fixtures_for_membership +from utils.tests import load_test_fixtures_for_membership, test_fixtures_membership class MembershipTest(ResourceTestCase): membership_url = '/api/v1/membership/' membership_url_single = '/api/v1/membership/{}/' + fixtures = test_fixtures_membership() + + def setUp(self): - load_test_fixtures_for_membership() + #load_test_fixtures_for_membership() # FIXME use fixtures above self.creds = self.create_apikey( - username=Member.objects.get(user_ptr_id=2).username, - api_key=ApiKey.objects.get(user=Member.objects.get(user_ptr_id=2)).key) + username=Member.objects.get(id=2).username, + api_key=ApiKey.objects.get(user=Member.objects.get(id=2)).key) super(MembershipTest, self).setUp() diff --git a/dusken/tests/support/test.py b/dusken/tests/support/test.py index e07fd5f..f050e3c 100644 --- a/dusken/tests/support/test.py +++ b/dusken/tests/support/test.py @@ -323,7 +323,7 @@ class ResourceTestCase(TestCase): """ Ensures the response is returning either a HTTP 202 or a HTTP 204. """ - return self.assertTrue(resp.status_code in [202, 204]) + return self.assertIn(resp.status_code, [202, 204]) def assertHttpMultipleChoices(self, resp): """ diff --git a/requirements.txt b/requirements.txt index 398e727..f4f5925 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,7 @@ -Django==1.5 -South==0.7.6 -django-tastypie==0.9.14 -psycopg2==2.4.5 -python-dateutil==1.5 -django-extensions==1.1.1 -django-sphinxdoc==1.1 -sphinxcontrib-httpdomain==1.1.8 +Django==1.5.4 +South==0.8.2 +django-tastypie==0.10.0 +psycopg2==2.5.1 +django-extensions==1.2.2 +django-sphinxdoc==1.2.0 +sphinxcontrib-httpdomain==1.1.9 diff --git a/utils/tests.py b/utils/tests.py index 56c8986..1daf52b 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -2,7 +2,6 @@ import os.path from django.core.management import call_command import django from dusken.models import * -from tastypie.models import create_api_key #################################################################### ### Request helper functions @@ -55,5 +54,6 @@ def test_fixtures_membership(): def load_test_fixtures_for_membership(): django.db.models.signals.post_save.disconnect(create_api_key, sender=Member) + print test_fixtures_membership() for f in test_fixtures_membership(): call_command('loaddata', f, verbosity=0)