瀏覽代碼

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 年之前
父節點
當前提交
cf86da266a
共有 3 個文件被更改,包括 72 次插入15 次删除
  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 查看文件

@@ -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 查看文件

@@ -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 查看文件

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


Loading…
取消
儲存