+
--
cgit v1.2.3
From 76a7d35813b637bb199a0d388468f9265f8adaf2 Mon Sep 17 00:00:00 2001
From: Jamie Matthews
Date: Tue, 7 Feb 2012 11:08:55 +0000
Subject: Ensure duplicate "page" parameters are not created
Previously, URLObject.add_query_param was used to generate
next/previous page links in PaginatorMixin. This resulted
in (for example) page 2's "next" link having the params:
?page=2&page=3
Instead, URLObject.set_query_param should be used to replace
the current value of the "page" parameter.
---
djangorestframework/mixins.py | 2 +-
djangorestframework/tests/mixins.py | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
index f4a9c998..836c3a59 100644
--- a/djangorestframework/mixins.py
+++ b/djangorestframework/mixins.py
@@ -679,7 +679,7 @@ class PaginatorMixin(object):
Constructs a url used for getting the next/previous urls
"""
url = URLObject.parse(self.request.get_full_path())
- url = url.add_query_param('page', page_number)
+ url = url.set_query_param('page', page_number)
limit = self.get_limit()
if limit != self.limit:
diff --git a/djangorestframework/tests/mixins.py b/djangorestframework/tests/mixins.py
index a7512efc..8268fdca 100644
--- a/djangorestframework/tests/mixins.py
+++ b/djangorestframework/tests/mixins.py
@@ -280,3 +280,12 @@ class TestPagination(TestCase):
self.assertTrue('foo=bar' in content['next'])
self.assertTrue('another=something' in content['next'])
self.assertTrue('page=2' in content['next'])
+
+ def test_duplicate_parameters_are_not_created(self):
+ """ Regression: ensure duplicate "page" parameters are not added to
+ paginated URLs. So page 1 should contain ?page=2, not ?page=1&page=2 """
+ request = self.req.get('/paginator/?page=1')
+ response = MockPaginatorView.as_view()(request)
+ content = json.loads(response.content)
+ self.assertTrue('page=2' in content['next'])
+ self.assertFalse('page=1' in content['next'])
--
cgit v1.2.3
From bc80eb266f071e0c090fcf882722d4dd056ccf61 Mon Sep 17 00:00:00 2001
From: Camille Harang
Date: Sat, 11 Feb 2012 01:49:28 +0100
Subject: DjangoModelPermisson
---
djangorestframework/permissions.py | 40 ++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index dfe55ce9..100a976e 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -89,6 +89,46 @@ class IsUserOrIsAnonReadOnly(BasePermission):
raise _403_FORBIDDEN_RESPONSE
+class DjangoModelPermisson(BasePermission):
+ """
+ """
+
+ def check_permission(self, user):
+
+ # GET-style methods are always allowed.
+ if self.view.request.method in ('GET', 'OPTIONS', 'HEAD',):
+ return
+
+ # User must be logged in to check permissions.
+ if not hasattr(self.view.request, 'user') or not self.view.request.user.is_authenticated():
+ raise _403_FORBIDDEN_RESPONSE
+
+ klass = self.view.resource.model
+
+ # If it doesn't look like a model, we can't check permissions.
+ if not klass or not getattr(klass, '_meta', None):
+ return
+
+ permission_map = {
+ 'POST': ['%s.add_%s'],
+ 'PUT': ['%s.change_%s'],
+ 'DELETE': ['%s.delete_%s'],
+ 'PATCH': ['%s.add_%s', '%s.change_%s', '%s.delete_%s'],
+ }
+ permission_codes = []
+
+ # If we don't recognize the HTTP method, we don't know what
+ # permissions to check. Deny.
+ if self.view.request.method not in permission_map:
+ raise _403_FORBIDDEN_RESPONSE
+
+ for perm in permission_map[self.view.request.method]:
+ permission_codes.append(perm % (klass._meta.app_label, klass._meta.module_name))
+
+ if not self.view.request.user.has_perms(permission_codes):
+ raise _403_FORBIDDEN_RESPONSE
+
+
class BaseThrottle(BasePermission):
"""
Rate throttling of requests.
--
cgit v1.2.3
From b236241982b95a35cdb251e5020004050fb6567a Mon Sep 17 00:00:00 2001
From: Camille Harang
Date: Sat, 11 Feb 2012 01:54:28 +0100
Subject: check authentication after checking ModelResource
---
djangorestframework/permissions.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index 100a976e..92e90fc3 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -99,16 +99,16 @@ class DjangoModelPermisson(BasePermission):
if self.view.request.method in ('GET', 'OPTIONS', 'HEAD',):
return
- # User must be logged in to check permissions.
- if not hasattr(self.view.request, 'user') or not self.view.request.user.is_authenticated():
- raise _403_FORBIDDEN_RESPONSE
-
klass = self.view.resource.model
# If it doesn't look like a model, we can't check permissions.
if not klass or not getattr(klass, '_meta', None):
return
+ # User must be logged in to check permissions.
+ if not hasattr(self.view.request, 'user') or not self.view.request.user.is_authenticated():
+ raise _403_FORBIDDEN_RESPONSE
+
permission_map = {
'POST': ['%s.add_%s'],
'PUT': ['%s.change_%s'],
--
cgit v1.2.3
From 963d2ecccbe30ca231621f85681049983248d08d Mon Sep 17 00:00:00 2001
From: Camille Harang
Date: Sat, 11 Feb 2012 02:02:42 +0100
Subject: DjangoModelPermisson's desc
---
djangorestframework/permissions.py | 2 ++
1 file changed, 2 insertions(+)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index 92e90fc3..cf556dd6 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -91,6 +91,8 @@ class IsUserOrIsAnonReadOnly(BasePermission):
class DjangoModelPermisson(BasePermission):
"""
+ The request is authenticated against the Django user's permissions on the
+ `Resource`'s `Model`, if the resource is a `ModelResource`.
"""
def check_permission(self, user):
--
cgit v1.2.3
From 88561a4ee2762409810b1aa7f85bda923169b69d Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Sat, 11 Feb 2012 13:00:38 +0000
Subject: Fix up DjangoModelPermissions.
---
djangorestframework/permissions.py | 66 ++++++++++++++++++++------------------
1 file changed, 34 insertions(+), 32 deletions(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index cf556dd6..7c61251d 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -89,45 +89,47 @@ class IsUserOrIsAnonReadOnly(BasePermission):
raise _403_FORBIDDEN_RESPONSE
-class DjangoModelPermisson(BasePermission):
+class DjangoModelPermission(BasePermission):
"""
The request is authenticated against the Django user's permissions on the
- `Resource`'s `Model`, if the resource is a `ModelResource`.
- """
-
- def check_permission(self, user):
-
- # GET-style methods are always allowed.
- if self.view.request.method in ('GET', 'OPTIONS', 'HEAD',):
- return
+ `Resource`'s `Model`.
- klass = self.view.resource.model
-
- # If it doesn't look like a model, we can't check permissions.
- if not klass or not getattr(klass, '_meta', None):
- return
-
- # User must be logged in to check permissions.
- if not hasattr(self.view.request, 'user') or not self.view.request.user.is_authenticated():
- raise _403_FORBIDDEN_RESPONSE
+ This permission should only be used on views with a `ModelResource`.
+ """
- permission_map = {
- 'POST': ['%s.add_%s'],
- 'PUT': ['%s.change_%s'],
- 'DELETE': ['%s.delete_%s'],
- 'PATCH': ['%s.add_%s', '%s.change_%s', '%s.delete_%s'],
+ # Map methods into required permission codes.
+ # Override this if you need to also provide 'read' permissions,
+ # or other custom behaviour.
+ perms_map = {
+ 'GET': [],
+ 'OPTIONS': [],
+ 'HEAD': [],
+ 'POST': ['%(app_label)s.add_%(model_name)s'],
+ 'PUT': ['%(app_label)s.change_%(model_name)s'],
+ 'PATCH': ['%(app_label)s.change_%(model_name)s'],
+ 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
+ }
+
+ def get_required_permissions(self, method, model_cls):
+ """
+ Given a model and an HTTP method, return the list of permission
+ codes that the user is required to have.
+ """
+ kwargs = {
+ 'app_label': model_cls._meta.app_label,
+ 'model_name': model_cls.__name__.lower()
}
- permission_codes = []
+ try:
+ return [perm % kwargs for perm in self.perms_map[method]]
+ except KeyError:
+ ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED)
- # If we don't recognize the HTTP method, we don't know what
- # permissions to check. Deny.
- if self.view.request.method not in permission_map:
- raise _403_FORBIDDEN_RESPONSE
-
- for perm in permission_map[self.view.request.method]:
- permission_codes.append(perm % (klass._meta.app_label, klass._meta.module_name))
+ def check_permission(self, user):
+ method = self.view.method
+ model_cls = self.view.resource.model
+ perms = self.get_required_permissions(method, model_cls)
- if not self.view.request.user.has_perms(permission_codes):
+ if not user.has_perms(perms):
raise _403_FORBIDDEN_RESPONSE
--
cgit v1.2.3
From 2c11fd68f8d57b3675940d4d5bf04f815fe521a6 Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Sat, 11 Feb 2012 17:48:35 +0000
Subject: Minor name change
---
djangorestframework/permissions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index 7c61251d..d47ba3dc 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -89,7 +89,7 @@ class IsUserOrIsAnonReadOnly(BasePermission):
raise _403_FORBIDDEN_RESPONSE
-class DjangoModelPermission(BasePermission):
+class DjangoModelPermissions(BasePermission):
"""
The request is authenticated against the Django user's permissions on the
`Resource`'s `Model`.
--
cgit v1.2.3
From cb8d94b956c5a39f15c54f7662bdbd2275ee3e4d Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Sat, 11 Feb 2012 18:29:24 +0000
Subject: Improve docstring on DjangoModelPermissions, and also ensure the user
is authenticated.
---
djangorestframework/permissions.py | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index d47ba3dc..de24e23b 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -91,15 +91,18 @@ class IsUserOrIsAnonReadOnly(BasePermission):
class DjangoModelPermissions(BasePermission):
"""
- The request is authenticated against the Django user's permissions on the
- `Resource`'s `Model`.
+ The request is authenticated using `django.contrib.auth` permissions.
+ See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
- This permission should only be used on views with a `ModelResource`.
+ It ensures that the user is authenticated, and has the appropriate
+ `add`/`change`/`delete` permissions on the model.
+
+ This permission should only be used on views with a `ModelResource`.
"""
# Map methods into required permission codes.
# Override this if you need to also provide 'read' permissions,
- # or other custom behaviour.
+ # or if you want to provide custom permisson codes.
perms_map = {
'GET': [],
'OPTIONS': [],
@@ -117,7 +120,7 @@ class DjangoModelPermissions(BasePermission):
"""
kwargs = {
'app_label': model_cls._meta.app_label,
- 'model_name': model_cls.__name__.lower()
+ 'model_name': model_cls._meta.module_name
}
try:
return [perm % kwargs for perm in self.perms_map[method]]
@@ -129,7 +132,7 @@ class DjangoModelPermissions(BasePermission):
model_cls = self.view.resource.model
perms = self.get_required_permissions(method, model_cls)
- if not user.has_perms(perms):
+ if not user.is_authenticated or not user.has_perms(perms):
raise _403_FORBIDDEN_RESPONSE
--
cgit v1.2.3
From 1ec165f38c508d7ac4c158ec8d558c5d8f1bd15b Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Sat, 11 Feb 2012 18:43:58 +0000
Subject: `OPTIONS` is also a safe method.
---
djangorestframework/permissions.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index de24e23b..2d6d8922 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -20,6 +20,8 @@ __all__ = (
'PerResourceThrottling'
)
+SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
+
_403_FORBIDDEN_RESPONSE = ErrorResponse(
status.HTTP_403_FORBIDDEN,
@@ -84,8 +86,7 @@ class IsUserOrIsAnonReadOnly(BasePermission):
def check_permission(self, user):
if (not user.is_authenticated() and
- self.view.method != 'GET' and
- self.view.method != 'HEAD'):
+ self.view.method not in SAFE_METHODS):
raise _403_FORBIDDEN_RESPONSE
--
cgit v1.2.3
From ba1e3b46998254e12578d75428669751e105735b Mon Sep 17 00:00:00 2001
From: Tom Christie
Date: Sat, 11 Feb 2012 21:15:06 +0000
Subject: Fix typo.
---
djangorestframework/permissions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'djangorestframework')
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
index 2d6d8922..03d78c2e 100644
--- a/djangorestframework/permissions.py
+++ b/djangorestframework/permissions.py
@@ -103,7 +103,7 @@ class DjangoModelPermissions(BasePermission):
# Map methods into required permission codes.
# Override this if you need to also provide 'read' permissions,
- # or if you want to provide custom permisson codes.
+ # or if you want to provide custom permission codes.
perms_map = {
'GET': [],
'OPTIONS': [],
--
cgit v1.2.3