Fix: Fix permission check for tastypie / update to 0.14.1

This commit is contained in:
winkidney
2018-08-26 03:35:14 -07:00
parent 913a0ba08d
commit fe1439b2a2
5 changed files with 81 additions and 40 deletions

View File

@@ -14,7 +14,7 @@ requests = "*"
django-taggit = "*" django-taggit = "*"
django-braces = "*" django-braces = "*"
django-compressor = "*" django-compressor = "*"
django-tastypie = ">=0.13.0,<0.14" django-tastypie = "*"
mock = "*" mock = "*"
factory-boy = "<2.0,>=1.3" factory-boy = "<2.0,>=1.3"
gunicorn = "*" gunicorn = "*"

7
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "c632e45ac592ec9c159c042db8c52e221ae133ade94cd11fcfb124a5fd5b9dd0" "sha256": "68c12441be13a252f7fdd1b5532c7befdfa56fc8307ab75a2b197a1946a54bf2"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@@ -69,11 +69,10 @@
}, },
"django-tastypie": { "django-tastypie": {
"hashes": [ "hashes": [
"sha256:bd4eb8a9da6c2dd70c946edc460776a64f40fae01132520a26133361bd43bf3f", "sha256:1fbf61ec7467eec70bd1abcb14e3b1dc67e47cc3642ad16ed8a3709f4140678b"
"sha256:e404b9ac24ab400015047f5503fae1732d9e944a7152693fe902c0a905f250cc"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.13.3" "version": "==0.14.1"
}, },
"factory-boy": { "factory-boy": {
"hashes": [ "hashes": [

View File

@@ -10,36 +10,101 @@ from .models import Pin, Image
from users.models import User from users.models import User
def _is_pin_owner(obj_or_list, user):
assert obj_or_list is not None
if not isinstance(obj_or_list, (tuple, list)):
obj_or_list = (obj_or_list,)
results = tuple(
obj.submitter == user
for obj in obj_or_list
if isinstance(obj, Pin)
)
if len(results) <= 0:
raise ValueError(
"You should never check permission on %s with this function."
% obj_or_list
)
return all(results)
def _is_authenticated_and_owner(object_list, bundle):
if bundle.request.user.is_anonymous():
return object_list.none()
return object_list.filter(submitter=bundle.request.user)
class PinryAuthorization(DjangoAuthorization): class PinryAuthorization(DjangoAuthorization):
""" """
Pinry-specific Authorization backend with object-level permission checking. Pinry-specific Authorization backend with object-level permission checking.
""" """
def update_detail(self, object_list, bundle): def _is_obj_owner(self, object_list, bundle):
klass = self.base_checks(bundle.request, bundle.obj.__class__) klass = self.base_checks(bundle.request, bundle.obj.__class__)
if klass is False: if klass is False:
raise Unauthorized("You are not allowed to access that resource.") raise Unauthorized("You are not allowed to access that resource.")
return _is_pin_owner(bundle.obj, bundle.request.user)
permission = '%s.change_%s' % (klass._meta.app_label, klass._meta.model_name) def read_list(self, object_list, bundle):
# This assumes a ``QuerySet`` from ``ModelResource``.
if not bundle.request.user.has_perm(permission, bundle.obj): return object_list
raise Unauthorized("You are not allowed to access that resource.")
def read_detail(self, object_list, bundle):
"""
User can always read detail of any Pin object.
"""
return True return True
def create_detail(self, object_list, bundle):
return self._is_obj_owner(object_list, bundle)
def update_detail(self, object_list, bundle):
return self._is_obj_owner(object_list, bundle)
def delete_detail(self, object_list, bundle): def delete_detail(self, object_list, bundle):
klass = self.base_checks(bundle.request, bundle.obj.__class__) return self._is_obj_owner(object_list, bundle)
if klass is False: def update_list(self, object_list, bundle):
raise Unauthorized("You are not allowed to access that resource.") return _is_authenticated_and_owner(object_list, bundle)
permission = '%s.delete_%s' % (klass._meta.app_label, klass._meta.model_name) def delete_list(self, object_list, bundle):
return _is_authenticated_and_owner(object_list, bundle)
if not bundle.request.user.has_perm(permission, bundle.obj):
raise Unauthorized("You are not allowed to access that resource.")
class ImageAuthorization(DjangoAuthorization):
"""
Pinry-specific Authorization backend with object-level permission checking.
"""
def __init__(self):
DjangoAuthorization.__init__(self)
def read_list(self, object_list, bundle):
return object_list
def read_detail(self, object_list, bundle):
"""
User can always read detail of any Pin object.
"""
return True return True
def create_detail(self, object_list, bundle):
return bundle.request.user.is_authenticated()
def update_detail(self, object_list, bundle):
return bundle.request.user.is_authenticated()
def delete_detail(self, object_list, bundle):
return bundle.request.user.is_authenticated()
def update_list(self, object_list, bundle):
if not bundle.request.user.is_authenticated():
return object_list.none()
return object_list
def delete_list(self, object_list, bundle):
if not bundle.request.user.is_authenticated():
return object_list.none()
return object_list
class UserResource(ModelResource): class UserResource(ModelResource):
gravatar = fields.CharField(readonly=True) gravatar = fields.CharField(readonly=True)
@@ -101,7 +166,7 @@ class ImageResource(ModelResource):
include_resource_uri = False include_resource_uri = False
resource_name = 'image' resource_name = 'image'
queryset = Image.objects.all() queryset = Image.objects.all()
authorization = DjangoAuthorization() authorization = ImageAuthorization()
class PinResource(ModelResource): class PinResource(ModelResource):

View File

@@ -33,11 +33,3 @@ class CombinedAuthBackend(object):
return User.objects.get(pk=user_id) return User.objects.get(pk=user_id)
except User.DoesNotExist: 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

View File

@@ -34,21 +34,6 @@ class CombinedAuthBackendTest(TestCase):
def test_authenticate_unknown_user(self): def test_authenticate_unknown_user(self):
self.assertIsNone(self.backend.authenticate(username='wrong-username', password='wrong-password')) self.assertIsNone(self.backend.authenticate(username='wrong-username', password='wrong-password'))
@mock.patch('requests.get', mock_requests_get)
def test_has_perm_on_pin(self):
image = Image.objects.create_for_url('http://testserver/mocked/screenshot.png')
user = User.objects.get(username=self.username)
pin = Pin.objects.create(submitter=user, image=image)
self.assertTrue(self.backend.has_perm(user, 'add_pin', pin))
@mock.patch('requests.get', mock_requests_get)
def test_has_perm_on_pin_unauthorized(self):
image = Image.objects.create_for_url('http://testserver/mocked/screenshot.png')
user = User.objects.get(username=self.username)
other_user = User.objects.create_user('test', 'test@example.com', 'test')
pin = Pin.objects.create(submitter=user, image=image)
self.assertFalse(self.backend.has_perm(other_user, 'add_pin', pin))
class CreateUserTest(TestCase): class CreateUserTest(TestCase):
def test_create_post(self): def test_create_post(self):