Selaa lähdekoodia

Add a very simplistic Pin access control for the API

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
Krzysztof Klimonda 11 vuotta sitten
vanhempi
commit
cf86da266a
3 muutettua tiedostoa jossa 72 lisäystä ja 15 poistoa
  1. +33
    -1
      pinry/core/api.py
  2. +11
    -1
      pinry/core/auth/backends.py
  3. +28
    -13
      pinry/core/tests.py

+ 33
- 1
pinry/core/api.py Näytä tiedosto

@@ -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()

+ 11
- 1
pinry/core/auth/backends.py Näytä tiedosto

@@ -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

+ 28
- 13
pinry/core/tests.py Näytä tiedosto

@@ -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')


Ladataan…
Peruuta
Tallenna