Skip to content

Commit 07e9c1b

Browse files
Alessio FabianiMatthew Northcottt-book
authored
[Fixes GeoNode#6685] GNIP-79: GeoNode REST APIs (v2) (GeoNode#6686)
* [WIP] GeoNode API v2 - Prototype * [ref. GeoNode#6388] Error when installing core GeoNode into virtual environment * - GeoNode REST APIs v2 - Swagger schema * [ref. GeoNode#6388] Error when installing core GeoNode into virtual environment * - GeoNode REST API v2 : Map and MapLayers endpoints * - GeoNode REST API v2 : Adding workflow ResourceBase metadata fields * [Dependencies] Removing conflicts * - Minor fixes and improvements * - Improving ResourceBase REST api permissions * - Improve Dynamic REST Sorting/Filtering * - Expose more metadata fields * - Expose more polymorphic_ctype_id field for filtering * - Improve Metadata Fields serialization in order to make it searchable/filterable * - Updating style sheets * - Introducing "DynamicSearchFilter" exposing search_fields * Merge branch 'master' of https://github.com/GeoNode/geonode into rest_api_v2_proof_of_concept # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit. * - Allow gs proxy to parse workspace prefix dynamically * - Downgrade pyproj to 2.6.1 * Revert " - Downgrade pyproj to 2.6.1" This reverts commit a702740. * - Improve Dockerfile * [Issue GeoNode#6414] Use Django storage API in delete_orphaned_* functions * [Issue GeoNode#6414] Use Django storage API in geonode.qgis_server.tests.test_views * [Issue GeoNode#6414] Use Django storage API when generating document thumbnails * [Issue GeoNode#6414] Thumbnail generation fix for local storage * Add thumbnail convenience functions * Cleanup Django storage API changes * [Hardening] Minor improvements and error checks * [Minor] Headers and formatting files * - Adding "OAuth2Authentication" to the default REST APIs auth_classes * - Fix "get_perms" api issue with non existant keys * - Fix "get_perms" api issue with non existant keys * [APIs] superusers have always perms to change resources * [Dependencies] bump djangorestframework to >=3.1.0,<3.12.0 * - Improve IsOwnerOrReadOnly REST permission class * - Improve IsOwnerOrReadOnly REST permission class * - Improve IsOwnerOrReadOnly REST permission class * - Fix Recenet Activity List for Documents * [Hardening] - Recenet Activity List for Documents error when actor is None * [Frontend] Monitoring: Bump "node-sass" to version 4.14.1 * [Frontend] Bump jquery to version 3.5.1 * [Fixes: GeoNode#6519] Bump jquery to 3.5.1 (GeoNode#6526) (cherry picked from commit e532813) # Conflicts: # geonode/static/lib/css/assets.min.css # geonode/static/lib/css/bootstrap-select.css # geonode/static/lib/css/bootstrap-table.css # geonode/static/lib/js/assets.min.js # geonode/static/lib/js/bootstrap-select.js # geonode/static/lib/js/bootstrap-table.js # geonode/static/lib/js/leaflet-plugins.min.js # geonode/static/lib/js/leaflet.js # geonode/static/lib/js/moment-timezone-with-data.js # geonode/static/lib/js/underscore.js * Merge branch 'master' of https://github.com/GeoNode/geonode into rest_api_v2_proof_of_concept # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit. * [Hardening] Re-create the map thumbnail only if it is missing * Fixes error with GDAL 3.0.4 due to a breaking change on GDAL (https://code.djangoproject.com/ticket/30645) * Fixes error with GDAL 3.0.4 due to a breaking change on GDAL (https://code.djangoproject.com/ticket/30645) * - Introducing REST APIs for "Docuemnts" resources too * - Bump django_mapstore_adapter to 2.0.6 / Bump django-geonode-mapstore-client to 2.0.9 * - Travis and LGTM fixes * - Swagger OAS3 * - Adding REST API v2 Test Suite * - Implementing API v2 "extent" filter * - Travis and LGTM fixes Co-authored-by: Matthew Northcott <[email protected]> Co-authored-by: Toni <[email protected]>
1 parent af7183a commit 07e9c1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2344
-83
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,5 @@ scripts/spcgeonode/_volume_*
9797
/geonode/development.db-journal
9898

9999
/output.bin
100+
101+
/worker.state

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
- ON_TRAVIS: 'True'
8484
- TEST_RUNNER_KEEPDB: 'True'
8585
- TEST_RUN_INTEGRATION: 'True'
86-
- TEST_RUN_INTEGRATION_SERVER: 'True'
86+
- TEST_RUN_INTEGRATION_SERVER: 'False'
8787
- TEST_RUN_INTEGRATION_UPLOAD: 'False'
8888
- TEST_RUN_INTEGRATION_MONITORING: 'False'
8989
- TEST_RUN_INTEGRATION_CSW: 'True'

geonode/api/urls.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@
1717
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
#
1919
#########################################################################
20-
2120
from tastypie.api import Api
21+
from dynamic_rest import routers
22+
from drf_spectacular.views import (
23+
SpectacularAPIView,
24+
SpectacularRedocView,
25+
SpectacularSwaggerView
26+
)
27+
28+
from django.urls import path
2229

2330
from . import api as resources
2431
from . import resourcebase_api as resourcebase_resources
@@ -40,3 +47,11 @@
4047
api.register(resourcebase_resources.LayerResource())
4148
api.register(resourcebase_resources.MapResource())
4249
api.register(resourcebase_resources.ResourceBaseResource())
50+
51+
router = routers.DynamicRouter()
52+
53+
urlpatterns = [
54+
path('schema/', SpectacularAPIView.as_view(), name='schema'),
55+
path('schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
56+
path('schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
57+
]

geonode/base/api/__init__.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# -*- coding: utf-8 -*-
2+
#########################################################################
3+
#
4+
# Copyright (C) 2020 OSGeo
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
#########################################################################

geonode/base/api/filters.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# -*- coding: utf-8 -*-
2+
#########################################################################
3+
#
4+
# Copyright (C) 2020 OSGeo
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
#########################################################################
20+
import logging
21+
22+
from rest_framework.filters import SearchFilter, BaseFilterBackend
23+
24+
from geonode.base.bbox_utils import filter_bbox
25+
26+
logger = logging.getLogger(__name__)
27+
28+
29+
class DynamicSearchFilter(SearchFilter):
30+
31+
def get_search_fields(self, view, request):
32+
return request.GET.getlist('search_fields', [])
33+
34+
35+
class ExtentFilter(BaseFilterBackend):
36+
"""
37+
Filter that only allows users to see their own objects.
38+
"""
39+
40+
def filter_queryset(self, request, queryset, view):
41+
if request.query_params.get('extent'):
42+
return filter_bbox(queryset, request.query_params.get('extent'))
43+
return queryset

geonode/base/api/pagination.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# -*- coding: utf-8 -*-
2+
#########################################################################
3+
#
4+
# Copyright (C) 2020 OSGeo
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
#########################################################################
20+
from django.conf import settings
21+
from rest_framework.response import Response
22+
from rest_framework.pagination import PageNumberPagination
23+
24+
DEFAULT_PAGE = getattr(settings, 'REST_API_DEFAULT_PAGE', 1)
25+
DEFAULT_PAGE_SIZE = getattr(settings, 'REST_API_DEFAULT_PAGE_SIZE', 10)
26+
DEFAULT_PAGE_QUERY_PARAM = getattr(settings, 'REST_API_DEFAULT_PAGE_QUERY_PARAM', 'page_size')
27+
28+
29+
class GeoNodeApiPagination(PageNumberPagination):
30+
31+
page = DEFAULT_PAGE
32+
page_size = DEFAULT_PAGE_SIZE
33+
page_size_query_param = DEFAULT_PAGE_QUERY_PARAM
34+
35+
def get_paginated_response(self, data):
36+
_paginated_response = {
37+
'links': {
38+
'next': self.get_next_link(),
39+
'previous': self.get_previous_link()
40+
},
41+
'total': self.page.paginator.count,
42+
'page': int(self.request.GET.get('page', DEFAULT_PAGE)), # can not set default = self.page
43+
DEFAULT_PAGE_QUERY_PARAM: int(self.request.GET.get(DEFAULT_PAGE_QUERY_PARAM, self.page_size))
44+
}
45+
_paginated_response.update(data)
46+
return Response(_paginated_response)

geonode/base/api/permissions.py

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# -*- coding: utf-8 -*-
2+
#########################################################################
3+
#
4+
# Copyright (C) 2020 OSGeo
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
#########################################################################
20+
import logging
21+
from django.conf import settings
22+
from rest_framework import permissions
23+
from rest_framework.filters import BaseFilterBackend
24+
25+
logger = logging.getLogger(__name__)
26+
27+
28+
class IsOwnerOrReadOnly(permissions.BasePermission):
29+
"""
30+
Object-level permission to only allow owners of an object to edit it.
31+
Assumes the model instance has an `owner` attribute.
32+
"""
33+
def has_object_permission(self, request, view, obj):
34+
if request.user is None or \
35+
(not request.user.is_anonymous and not request.user.is_active):
36+
return False
37+
if request.user.is_superuser:
38+
return True
39+
40+
# Read permissions are allowed to any request,
41+
# so we'll always allow GET, HEAD or OPTIONS requests.
42+
if request.method in permissions.SAFE_METHODS:
43+
return True
44+
45+
# Instance must have an attribute named `owner`.
46+
if hasattr(obj, 'owner'):
47+
return obj.owner == request.user
48+
if hasattr(obj, 'user'):
49+
return obj.user == request.user
50+
else:
51+
return False
52+
53+
54+
class ResourceBasePermissionsFilter(BaseFilterBackend):
55+
"""
56+
A filter backend that limits results to those where the requesting user
57+
has read object level permissions.
58+
"""
59+
shortcut_kwargs = {
60+
'accept_global_perms': True,
61+
}
62+
63+
def filter_queryset(self, request, queryset, view):
64+
# We want to defer this import until runtime, rather than import-time.
65+
# See https://github.com/encode/django-rest-framework/issues/4608
66+
# (Also see #1624 for why we need to make this import explicitly)
67+
from guardian.shortcuts import get_objects_for_user
68+
from geonode.base.models import ResourceBase
69+
from geonode.security.utils import get_visible_resources
70+
71+
user = request.user
72+
# perm_format = '%(app_label)s.view_%(model_name)s'
73+
# permission = self.perm_format % {
74+
# 'app_label': queryset.model._meta.app_label,
75+
# 'model_name': queryset.model._meta.model_name,
76+
# }
77+
78+
if settings.SKIP_PERMS_FILTER:
79+
resources = ResourceBase.objects.all()
80+
else:
81+
resources = get_objects_for_user(
82+
user,
83+
'base.view_resourcebase',
84+
**self.shortcut_kwargs
85+
)
86+
logger.debug(f" user: {user} -- resources: {resources}")
87+
88+
obj_with_perms = get_visible_resources(
89+
resources,
90+
user,
91+
admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
92+
unpublished_not_visible=settings.RESOURCE_PUBLISHING,
93+
private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES)
94+
logger.debug(f" user: {user} -- obj_with_perms: {obj_with_perms}")
95+
96+
return queryset.filter(id__in=obj_with_perms.values('id'))

0 commit comments

Comments
 (0)