As pointed in issue #75 we should get away with just checking if the pin submitter is the currently logged in user. Assuming that we can implement authorization for updating and deleting pins rather easily by subclassing DjangoAuthorization so it passes the object to the Authorization backend.tags/v1.0.0
@@ -1,5 +1,6 @@ | |||
from tastypie import fields | |||
from tastypie.authorization import DjangoAuthorization | |||
from tastypie.exceptions import Unauthorized | |||
from tastypie.resources import ModelResource | |||
from django_images.models import Thumbnail | |||
@@ -7,6 +8,37 @@ from pinry.core.models import User | |||
from pinry.pins.models import Image, Pin | |||
class PinryAuthorization(DjangoAuthorization): | |||
""" | |||
Pinry-specific Authorization backend with object-level permission checking. | |||
""" | |||
def update_detail(self, object_list, bundle): | |||
klass = self.base_checks(bundle.request, bundle.obj.__class__) | |||
if klass is False: | |||
raise Unauthorized("You are not allowed to access that resource.") | |||
permission = '%s.change_%s' % (klass._meta.app_label, klass._meta.module_name) | |||
if not bundle.request.user.has_perm(permission, bundle.obj): | |||
raise Unauthorized("You are not allowed to access that resource.") | |||
return True | |||
def delete_detail(self, object_list, bundle): | |||
klass = self.base_checks(bundle.request, bundle.obj.__class__) | |||
if klass is False: | |||
raise Unauthorized("You are not allowed to access that resource.") | |||
permission = '%s.delete_%s' % (klass._meta.app_label, klass._meta.module_name) | |||
if not bundle.request.user.has_perm(permission, bundle.obj): | |||
raise Unauthorized("You are not allowed to access that resource.") | |||
return True | |||
class UserResource(ModelResource): | |||
gravatar = fields.CharField(readonly=True) | |||
@@ -87,4 +119,4 @@ class PinResource(ModelResource): | |||
resource_name = 'pin' | |||
include_resource_uri = False | |||
always_return_data = True | |||
authorization = DjangoAuthorization() | |||
authorization = PinryAuthorization() |
@@ -1,6 +1,8 @@ | |||
from django.core.validators import email_re | |||
from pinry.core.models import User | |||
from pinry.pins.models import Pin | |||
class CombinedAuthBackend(object): | |||
def authenticate(self, username=None, password=None): | |||
@@ -22,4 +24,12 @@ class CombinedAuthBackend(object): | |||
try: | |||
return User.objects.get(pk=user_id) | |||
except User.DoesNotExist: | |||
return None | |||
return None | |||
def has_perm(self, user, perm, obj=None): | |||
""" | |||
A very simplistic authorization mechanism for now. Basically a pin owner can do anything with the pin. | |||
""" | |||
if obj and isinstance(obj, Pin): | |||
return obj.submitter == user | |||
return False |
@@ -37,7 +37,6 @@ class ImageResourceTest(ResourceTestCase): | |||
self.client = Client() | |||
def test_list_detail(self): | |||
self.maxDiff = None | |||
image = Image.objects.get(pk=1) | |||
thumbnail = filter_generator_for('thumbnail')(image) | |||
standard = filter_generator_for('standard')(image) | |||
@@ -107,28 +106,44 @@ class PinResourceTest(ResourceTestCase): | |||
self.assertEqual(Pin.objects.count(), 3) | |||
self.assertEquals(Tag.objects.count(), 4) | |||
def test_put_details_unauthenticated(self): | |||
def test_put_detail_unauthenticated(self): | |||
self.api_client.client.logout() | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
response = self.api_client.put(uri, format='json', data={}) | |||
self.assertHttpUnauthorized(response) | |||
def test_put_details_unauthorized(self): | |||
def test_put_detail_unauthorized(self): | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
User.objects.create_user('test', 'test@example.com', 'test') | |||
self.api_client.client.login(username='test', password='test') | |||
response = self.api_client.put(uri, format='json', data={}) | |||
self.assertHttpUnauthorized(response) | |||
# def test_put_details(self): | |||
# uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
# original = self.deserialize(self.api_client.get(uri, format='json')) | |||
# new = original.copy() | |||
# new['description'] = 'Updated description' | |||
# | |||
# self.assertEqual(Pin.objects.count(), 2) | |||
# response = self.api_client.put(uri, format='json', data=new) | |||
# self.assertHttpAccepted(response) | |||
# self.assertEqual(Pin.objects.count(), 2) | |||
def test_put_detail(self): | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
original = self.deserialize(self.api_client.get(uri, format='json')) | |||
new = {'description': 'Updated description'} | |||
response = self.api_client.put(uri, format='json', data=new) | |||
self.assertHttpAccepted(response) | |||
self.assertEqual(Pin.objects.count(), 2) | |||
self.assertEqual(Pin.objects.get(pk=self.pin_1.pk).description, new['description']) | |||
def test_delete_detail_unauthenticated(self): | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
self.api_client.client.logout() | |||
self.assertHttpUnauthorized(self.api_client.delete(uri)) | |||
def test_delete_detail_unauthorized(self): | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
User.objects.create_user('test', 'test@example.com', 'test') | |||
self.api_client.client.login(username='test', password='test') | |||
self.assertHttpUnauthorized(self.api_client.delete(uri)) | |||
def test_delete_detail(self): | |||
uri = '/api/v1/pin/{}/'.format(self.pin_1.pk) | |||
self.assertHttpAccepted(self.api_client.delete(uri)) | |||
self.assertEqual(Pin.objects.count(), 1) | |||
def test_get_list_json_ordered(self): | |||
pin = Pin.objects.latest('id') | |||