Skip to content

Commit

Permalink
[Fixes #3951] [Cross-site scripting test - Security related - Issue] …
Browse files Browse the repository at this point in the history
…Improvements to Tastypie paginator
  • Loading branch information
afabiani committed Oct 9, 2018
1 parent 41c59f2 commit 4337f0e
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
3 changes: 3 additions & 0 deletions geonode/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from tastypie.exceptions import BadRequest

from geonode import qgis_server, geoserver
from geonode.api.paginator import CrossSiteXHRPaginator
from geonode.api.authorization import GeoNodeStyleAuthorization
from geonode.qgis_server.models import QGISServerStyle
from guardian.shortcuts import get_objects_for_user
Expand Down Expand Up @@ -520,6 +521,7 @@ class QGISStyleResource(ModelResource):
type = fields.CharField(attribute='type')

class Meta:
paginator_class = CrossSiteXHRPaginator
queryset = QGISServerStyle.objects.all()
resource_name = 'styles'
detail_uri_name = 'id'
Expand Down Expand Up @@ -699,6 +701,7 @@ class GeoserverStyleResource(ModelResource):
type = fields.CharField(attribute='type')

class Meta:
paginator_class = CrossSiteXHRPaginator
queryset = Style.objects.all()
resource_name = 'styles'
detail_uri_name = 'id'
Expand Down
82 changes: 82 additions & 0 deletions geonode/api/paginator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# Copyright (C) 2018 OSGeo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################

from django.conf import settings
from tastypie.exceptions import BadRequest
from tastypie.paginator import Paginator


class CrossSiteXHRPaginator(Paginator):

def get_limit(self):
"""
Determines the proper maximum number of results to return.
In order of importance, it will use:
* The user-requested ``limit`` from the GET parameters, if specified.
* The object-level ``limit`` if specified.
* ``settings.API_LIMIT_PER_PAGE`` if specified.
Default is 20 per page.
"""

limit = self.request_data.get('limit', self.limit)
if limit is None:
limit = getattr(settings, 'API_LIMIT_PER_PAGE', 20)

try:
limit = int(limit)
except ValueError:
raise BadRequest("Invalid limit provided. Please provide a positive integer.")

if limit < 0:
raise BadRequest("Invalid limit provided. Please provide a positive integer >= 0.")

if self.max_limit and (not limit or limit > self.max_limit):
# If it's more than the max, we're only going to return the max.
# This is to prevent excessive DB (or other) load.
return self.max_limit

return limit

def get_offset(self):
"""
Determines the proper starting offset of results to return.
It attempts to use the user-provided ``offset`` from the GET parameters,
if specified. Otherwise, it falls back to the object-level ``offset``.
Default is 0.
"""
offset = self.offset

if 'offset' in self.request_data:
offset = self.request_data['offset']

try:
offset = int(offset)
except ValueError:
raise BadRequest("Invalid offset provided. Please provide an integer.")

if offset < 0:
raise BadRequest("Invalid offset provided. Please provide a positive integer >= 0.")

return offset
17 changes: 13 additions & 4 deletions geonode/api/resourcebase_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@

from .authorization import GeoNodeAuthorization, GeonodeApiKeyAuthentication

from .api import TagResource, RegionResource, OwnersResource
from .api import ThesaurusKeywordResource
from .api import TopicCategoryResource, GroupResource
from .api import FILTER_TYPES
from .api import (TagResource,
RegionResource,
OwnersResource,
ThesaurusKeywordResource,
TopicCategoryResource,
GroupResource,
FILTER_TYPES)
from .paginator import CrossSiteXHRPaginator

if settings.HAYSTACK_SEARCH:
from haystack.query import SearchQuerySet # noqa
Expand Down Expand Up @@ -650,6 +654,7 @@ class ResourceBaseResource(CommonModelApi):
"""ResourceBase api"""

class Meta(CommonMetaApi):
paginator_class = CrossSiteXHRPaginator
queryset = ResourceBase.objects.polymorphic_queryset() \
.distinct().order_by('-date')
resource_name = 'base'
Expand All @@ -662,6 +667,7 @@ class FeaturedResourceBaseResource(CommonModelApi):
"""Only the featured resourcebases"""

class Meta(CommonMetaApi):
paginator_class = CrossSiteXHRPaginator
queryset = ResourceBase.objects.filter(featured=True).order_by('-date')
resource_name = 'featured'
authentication = MultiAuthentication(SessionAuthentication(), GeonodeApiKeyAuthentication())
Expand Down Expand Up @@ -864,6 +870,7 @@ def patch_detail(self, request, **kwargs):
VALUES.append('typename')

class Meta(CommonMetaApi):
paginator_class = CrossSiteXHRPaginator
queryset = Layer.objects.distinct().order_by('-date')
resource_name = 'layers'
detail_uri_name = 'id'
Expand Down Expand Up @@ -941,6 +948,7 @@ def format_objects(self, objects):
return formatted_objects

class Meta(CommonMetaApi):
paginator_class = CrossSiteXHRPaginator
queryset = Map.objects.distinct().order_by('-date')
resource_name = 'maps'
authentication = MultiAuthentication(SessionAuthentication(), GeonodeApiKeyAuthentication())
Expand Down Expand Up @@ -985,6 +993,7 @@ def format_objects(self, objects):
return formatted_objects

class Meta(CommonMetaApi):
paginator_class = CrossSiteXHRPaginator
filtering = CommonMetaApi.filtering
filtering.update({'doc_type': ALL})
queryset = Document.objects.distinct().order_by('-date')
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ django-downloadview<=1.9 # python-django-downloadview (1.9 in our ppa)
django-polymorphic<2.0 # python-django-polymorphic (1.3)
django-reversion<=2.0.13 # python-django-reversion (2.0.13 in our ppa)
django-suit<=0.2.26 # python-django-suit (0.2.26 in our ppa)
django-tastypie<=0.14.0 # python-django-tastypie (0.14.0 in our ppa)
django-tastypie<=0.14.2 # python-django-tastypie (0.14.0 in our ppa)
django-invitations<=1.9.2 # python-django-invitations (1.9.2 in our ppa)
djangorestframework==3.8.2
# djangorestframework-gis<=0.12 # TODO
Expand Down

0 comments on commit 4337f0e

Please sign in to comment.