A massive amount of work upgrading the API to Tastypie >= 0.9.14 with new authorization schema.
parent
fb0142f249
commit
e669c5f25c
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.conf.urls import *
|
||||
|
||||
from accounts import views
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.conf.urls import *
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.blog, name='blog'),
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
},
|
||||
|
|
162
snipts/api.py
162
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.conf.urls import *
|
||||
|
||||
from snipts import views
|
||||
|
||||
|
|
2
urls.py
2
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
|
||||
|
|
Loading…
Reference in New Issue