diff --git a/accounts/urls.py b/accounts/urls.py index 6c6a78a..376cbbe 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import * from accounts import views diff --git a/blogs/urls.py b/blogs/urls.py index ccdcd39..a9219b5 100644 --- a/blogs/urls.py +++ b/blogs/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import * urlpatterns = patterns('', url(r'^$', views.blog, name='blog'), diff --git a/settings.py b/settings.py index ea6c17e..b441ddf 100644 --- a/settings.py +++ b/settings.py @@ -151,9 +151,15 @@ SESSION_COOKIE_AGE = 15801100 LOGGING = { 'version': 1, 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, 'handlers': { 'mail_admins': { 'level': 'ERROR', + 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' } }, diff --git a/snipts/api.py b/snipts/api.py index 27d72c8..4f478f2 100644 --- a/snipts/api.py +++ b/snipts/api.py @@ -3,6 +3,7 @@ from tastypie.authentication import ApiKeyAuthentication from tastypie.authorization import Authorization from django.template.defaultfilters import date, urlize, linebreaksbr from tastypie.resources import ModelResource +from tastypie.exceptions import Unauthorized from django.contrib.auth.models import User from tastypie.validation import Validation from tastypie.models import create_api_key @@ -22,8 +23,137 @@ import parsedatetime.parsedatetime as pdt models.signals.post_save.connect(create_api_key, sender=User) +class PrivateFavoriteAuthorization(Authorization): + def read_list(self, object_list, bundle): + return object_list.filter(user=bundle.request.user) + + def read_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def create_list(self, object_list, bundle): + raise Unauthorized() + + def create_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def update_list(self, object_list, bundle): + raise Unauthorized() + + def update_detail(self, object_list, bundle): + raise Unauthorized() + + def delete_list(self, object_list, bundle): + raise Unauthorized() + + def delete_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + +class PrivateSniptAuthorization(Authorization): + def read_list(self, object_list, bundle): + return object_list.filter(user=bundle.request.user) + + def read_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def create_list(self, object_list, bundle): + raise Unauthorized() + + def create_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def update_list(self, object_list, bundle): + return object_list.filter(user=bundle.request.user) + + def update_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def delete_list(self, object_list, bundle): + return object_list.filter(user=bundle.request.user) + + def delete_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + +class PrivateTagAuthorization(Authorization): + def read_list(self, object_list, bundle): + object_list = object_list.filter(snipt__user=bundle.request.user) + object_list = object_list.annotate(count=models.Count('taggit_taggeditem_items__id')) + object_list = object_list.order_by('-count', 'name') + return object_list + + def read_detail(self, object_list, bundle): + raise Unauthorized() + + def create_list(self, object_list, bundle): + raise Unauthorized() + + def create_detail(self, object_list, bundle): + raise Unauthorized() + + def update_list(self, object_list, bundle): + raise Unauthorized() + + def update_detail(self, object_list, bundle): + raise Unauthorized() + + def delete_list(self, object_list, bundle): + raise Unauthorized() + + def delete_detail(self, object_list, bundle): + raise Unauthorized() + +class PrivateUserProfileAuthorization(Authorization): + def read_list(self, object_list, bundle): + raise Unauthorized() + + def read_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def create_list(self, object_list, bundle): + raise Unauthorized() + + def create_detail(self, object_list, bundle): + raise Unauthorized() + + def update_list(self, object_list, bundle): + raise Unauthorized() + + def update_detail(self, object_list, bundle): + return bundle.obj.user == bundle.request.user + + def delete_list(self, object_list, bundle): + raise Unauthorized() + + def delete_detail(self, object_list, bundle): + raise Unauthorized() + +class PrivateUserAuthorization(Authorization): + def read_list(self, object_list, bundle): + raise Unauthorized() + + def read_detail(self, object_list, bundle): + return bundle.obj == bundle.request.user + + def create_list(self, object_list, bundle): + raise Unauthorized() + + def create_detail(self, object_list, bundle): + raise Unauthorized() + + def update_list(self, object_list, bundle): + raise Unauthorized() + + def update_detail(self, object_list, bundle): + raise Unauthorized() + + def delete_list(self, object_list, bundle): + raise Unauthorized() + + def delete_detail(self, object_list, bundle): + raise Unauthorized() + + class FavoriteValidation(Validation): - def is_valid(self, bundle): + def is_valid(self, bundle, request=None): errors = {} snipt = bundle.data['snipt'] @@ -33,7 +163,7 @@ class FavoriteValidation(Validation): return errors class UserProfileValidation(Validation): - def is_valid(self, bundle): + def is_valid(self, bundle, request=None): errors = {} if not bundle.request.user.profile.is_pro: @@ -142,13 +272,10 @@ class PrivateUserProfileResource(ModelResource): allowed_methods = ['get', 'put'] list_allowed_methods = [] authentication = ApiKeyAuthentication() - authorization = Authorization() + authorization = PrivateUserProfileAuthorization() always_return_data = True max_limit = 200 - def apply_authorization_limits(self, request, object_list): - return object_list.filter(user=request.user) - def dehydrate(self, bundle): bundle.data['email'] = bundle.obj.user.email bundle.data['username'] = bundle.obj.user.username @@ -168,14 +295,11 @@ class PrivateUserResource(ModelResource): allowed_methods = ['get'] list_allowed_methods = [] authentication = ApiKeyAuthentication() - authorization = Authorization() + authorization = PrivateUserAuthorization() always_return_data = True max_limit = 200 cache = SimpleCache() - def apply_authorization_limits(self, request, object_list): - return object_list.filter(username=request.user.username) - def dehydrate(self, bundle): bundle.data['email_md5'] = hashlib.md5(bundle.obj.email.lower()).hexdigest() bundle.data['is_pro'] = bundle.obj.profile.is_pro @@ -190,7 +314,7 @@ class PrivateTagResource(ModelResource): fields = ['id', 'name',] allowed_methods = ['get'] authentication = ApiKeyAuthentication() - authorization = Authorization() + authorization = PrivateTagAuthorization() always_return_data = True max_limit = 200 cache = SimpleCache() @@ -205,6 +329,7 @@ class PrivateTagResource(ModelResource): orm_filters['slug'] = filters['q'] return orm_filters + def dehydrate(self, bundle): bundle.data['absolute_url'] = '/%s/tag/%s/' % (bundle.request.user.username, bundle.obj.slug) @@ -216,11 +341,6 @@ class PrivateTagResource(ModelResource): return bundle - def apply_authorization_limits(self, request, object_list): - object_list = object_list.filter(snipt__user=request.user) - object_list = object_list.annotate(count=models.Count('taggit_taggeditem_items__id')) - object_list = object_list.order_by('-count', 'name') - return object_list class PrivateSniptResource(ModelResource): user = fields.ForeignKey(PrivateUserResource, 'user', full=True) @@ -236,7 +356,7 @@ class PrivateSniptResource(ModelResource): detail_allowed_methods = ['get', 'patch', 'put', 'delete'] list_allowed_methods = ['get', 'post'] authentication = ApiKeyAuthentication() - authorization = Authorization() + authorization = PrivateSniptAuthorization() ordering = ['created', 'modified',] always_return_data = True max_limit = 200 @@ -321,9 +441,6 @@ class PrivateSniptResource(ModelResource): return orm_filters - def apply_authorization_limits(self, request, object_list): - return object_list.filter(user=request.user) - def save_m2m(self, bundle): tags = bundle.data.get('tags_list', []) if tags != '': @@ -343,7 +460,7 @@ class PrivateFavoriteResource(ModelResource): detail_allowed_methods = ['get', 'post', 'delete'] list_allowed_methods = ['get', 'post',] authentication = ApiKeyAuthentication() - authorization = Authorization() + authorization = PrivateFavoriteAuthorization() ordering = ['created',] always_return_data = True max_limit = 200 @@ -359,6 +476,3 @@ class PrivateFavoriteResource(ModelResource): bundle.data['snipt'] = Snipt.objects.get(pk=bundle.data['snipt']) return super(PrivateFavoriteResource, self).obj_create(bundle, user=bundle.request.user, **kwargs) - - def apply_authorization_limits(self, request, object_list): - return object_list.filter(user=request.user) diff --git a/snipts/tests.py b/snipts/tests.py index 96915a8..602aee0 100644 --- a/snipts/tests.py +++ b/snipts/tests.py @@ -1,9 +1,89 @@ -from django.test import TestCase +from django.contrib.auth.models import User +from tastypie.test import ResourceTestCase +from tastypie.models import ApiKey +from snipts.models import Snipt -class SniptAPITest(TestCase): - def test_get_snipt(self): - """ - We should be able to get a public snipt from the API. - """ - self.assertEqual(1 + 1, 2) +class SniptResourceTest(ResourceTestCase): + fixtures = ['test_entries.json'] + + def setUp(self): + super(SniptResourceTest, self).setUp() + + # Johnny + self.johnny = User.objects.create_user('johnny', 'johnny@snipt.net', 'password') + ApiKey.objects.get_or_create(user=self.johnny) + self.johnny_auth = self.create_apikey(self.johnny, self.johnny.api_key.key) + self.johnny_private = Snipt(title='Private snipt for Johnny', lexer='text', public=False, user=self.johnny) + self.johnny_public = Snipt(title='Public snipt for Johnny', lexer='text', public=True, user=self.johnny) + self.johnny_private.save() + self.johnny_public.save() + + # Bob + self.bob = User.objects.create_user('bob', 'bob@snipt.net', 'password') + ApiKey.objects.get_or_create(user=self.bob) + self.bob_auth = self.create_apikey(self.bob, self.bob.api_key.key) + self.bob_private = Snipt(title='Private snipt for Bob', lexer='text', public=False, user=self.bob) + self.bob_public = Snipt(title='Public snipt for Bob', lexer='text', public=True, user=self.bob) + self.bob_private.save() + self.bob_public.save() + + + # Private + def test_get_private_list(self): + + resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) + + self.assertHttpOK(resp) + self.assertValidJSONResponse(resp) + self.assertEqual(len(self.deserialize(resp)['objects']), 2) + + def test_get_private_detail(self): + + resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.johnny_auth) + + self.assertHttpOK(resp) + self.assertValidJSONResponse(resp) + self.assertEqual(self.deserialize(resp)['key'], self.johnny_private.key) + + # Unauthenticated request. + resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json') + self.assertHttpUnauthorized(resp) + + # Unauthorized request. + resp = self.api_client.get('/api/private/snipt/{}/'.format(self.johnny_private.pk), format='json', authentication=self.bob_auth) + self.assertHttpUnauthorized(resp) + + def test_post_private_list(self): + + new_snipt = { + 'title': 'A new private snipt.', + 'lexer': 'text', + 'public': False, + } + + resp = self.api_client.post('/api/private/snipt/', + data=new_snipt, + format='json', + authentication=self.johnny_auth) + + self.assertHttpCreated(resp) + self.assertEqual(Snipt.objects.count(), 5) + + resp = self.api_client.get('/api/private/snipt/', format='json', authentication=self.johnny_auth) + self.assertEqual(len(self.deserialize(resp)['objects']), 3) + + resp = self.api_client.get('/api/public/snipt/', format='json') + self.assertEqual(len(self.deserialize(resp)['objects']), 2) + + + # Public + def test_get_public_list(self): + + self.assertEqual(Snipt.objects.count(), 4) + + resp = self.api_client.get('/api/public/snipt/', format='json') + + self.assertHttpOK(resp) + self.assertValidJSONResponse(resp) + self.assertEqual(len(self.deserialize(resp)['objects']), 2) diff --git a/snipts/urls.py b/snipts/urls.py index de0570e..e4ea406 100644 --- a/snipts/urls.py +++ b/snipts/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import * from snipts import views diff --git a/urls.py b/urls.py index e0737f3..b0df480 100644 --- a/urls.py +++ b/urls.py @@ -1,5 +1,5 @@ from views import (homepage, lexers, pro_signup, sitemap, tags, pro_signup_complete) -from django.conf.urls.defaults import include, patterns, url +from django.conf.urls import include, patterns, url from utils.views import SniptRegistrationView from django.views.generic import TemplateView from django.http import HttpResponseRedirect