Refactor share_status check in datalayers views
This commit is contained in:
parent
9bcc18b790
commit
73d19e849f
7 changed files with 105 additions and 24 deletions
|
@ -47,6 +47,22 @@ def map_permissions_check(view_func):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def can_view_map(view_func):
|
||||||
|
"""
|
||||||
|
Used for URLs dealing with viewing the map.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
map_inst = get_object_or_404(Map, pk=kwargs["map_id"])
|
||||||
|
kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view
|
||||||
|
if not map_inst.can_view(request):
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
return view_func(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def jsonize_view(view_func):
|
def jsonize_view(view_func):
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
|
|
|
@ -193,7 +193,7 @@ class Map(NamedModel):
|
||||||
public = PublicManager()
|
public = PublicManager()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("map", kwargs={"slug": self.slug or "map", "pk": self.pk})
|
return reverse("map", kwargs={"slug": self.slug or "map", "map_id": self.pk})
|
||||||
|
|
||||||
def get_anonymous_edit_url(self):
|
def get_anonymous_edit_url(self):
|
||||||
signer = Signer()
|
signer = Signer()
|
||||||
|
|
|
@ -5,6 +5,8 @@ import pytest
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.signing import get_cookie_signer
|
from django.core.signing import get_cookie_signer
|
||||||
|
|
||||||
|
from umap.models import Map
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
DataLayerFactory,
|
DataLayerFactory,
|
||||||
MapFactory,
|
MapFactory,
|
||||||
|
|
|
@ -23,9 +23,12 @@ def post_data():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_get(client, settings, datalayer):
|
def test_get_with_public_mode(client, settings, datalayer, map):
|
||||||
url = reverse("datalayer_view", args=(datalayer.pk,))
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
assert response["Last-Modified"] is not None
|
assert response["Last-Modified"] is not None
|
||||||
assert response["Cache-Control"] is not None
|
assert response["Cache-Control"] is not None
|
||||||
assert "Content-Encoding" not in response
|
assert "Content-Encoding" not in response
|
||||||
|
@ -35,8 +38,49 @@ def test_get(client, settings, datalayer):
|
||||||
assert j["type"] == "FeatureCollection"
|
assert j["type"] == "FeatureCollection"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_with_open_mode(client, settings, datalayer, map):
|
||||||
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_with_blocked_mode(client, settings, datalayer, map):
|
||||||
|
map.share_status = Map.BLOCKED
|
||||||
|
map.save()
|
||||||
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_cannot_get_datalayer_if_not_public(client, settings, datalayer, map):
|
||||||
|
map.share_status = Map.PRIVATE
|
||||||
|
map.save()
|
||||||
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_cannot_get_datalayer_if_private_and_owner(client, settings, datalayer, map):
|
||||||
|
map.share_status = Map.PRIVATE
|
||||||
|
map.save()
|
||||||
|
client.login(username=map.owner.username, password="123123")
|
||||||
|
url = reverse(
|
||||||
|
"datalayer_view",
|
||||||
|
args=(
|
||||||
|
map.pk,
|
||||||
|
datalayer.pk,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
response = client.get(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_gzip_should_be_created_if_accepted(client, datalayer, map, post_data):
|
def test_gzip_should_be_created_if_accepted(client, datalayer, map, post_data):
|
||||||
url = reverse("datalayer_view", args=(datalayer.pk,))
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
response = client.get(url, headers={"ACCEPT_ENCODING": "gzip"})
|
response = client.get(url, headers={"ACCEPT_ENCODING": "gzip"})
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
flat = datalayer.geojson.path
|
flat = datalayer.geojson.path
|
||||||
|
@ -107,8 +151,10 @@ def test_should_not_be_possible_to_delete_with_wrong_map_id_in_url(
|
||||||
def test_optimistic_concurrency_control_with_good_last_modified(
|
def test_optimistic_concurrency_control_with_good_last_modified(
|
||||||
client, datalayer, map, post_data
|
client, datalayer, map, post_data
|
||||||
):
|
):
|
||||||
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
# Get Last-Modified
|
# Get Last-Modified
|
||||||
url = reverse("datalayer_view", args=(datalayer.pk,))
|
url = reverse("datalayer_view", args=(map.pk, datalayer.pk))
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
last_modified = response["Last-Modified"]
|
last_modified = response["Last-Modified"]
|
||||||
url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
|
url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
|
||||||
|
@ -150,6 +196,8 @@ def test_optimistic_concurrency_control_with_empty_last_modified(
|
||||||
|
|
||||||
|
|
||||||
def test_versions_should_return_versions(client, datalayer, map, settings):
|
def test_versions_should_return_versions(client, datalayer, map, settings):
|
||||||
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
root = datalayer.storage_root()
|
root = datalayer.storage_root()
|
||||||
datalayer.geojson.storage.save(
|
datalayer.geojson.storage.save(
|
||||||
"%s/%s_1440924889.geojson" % (root, datalayer.pk), ContentFile("{}")
|
"%s/%s_1440924889.geojson" % (root, datalayer.pk), ContentFile("{}")
|
||||||
|
@ -160,7 +208,7 @@ def test_versions_should_return_versions(client, datalayer, map, settings):
|
||||||
datalayer.geojson.storage.save(
|
datalayer.geojson.storage.save(
|
||||||
"%s/%s_1440918637.geojson" % (root, datalayer.pk), ContentFile("{}")
|
"%s/%s_1440918637.geojson" % (root, datalayer.pk), ContentFile("{}")
|
||||||
)
|
)
|
||||||
url = reverse("datalayer_versions", args=(datalayer.pk,))
|
url = reverse("datalayer_versions", args=(map.pk, datalayer.pk))
|
||||||
versions = json.loads(client.get(url).content.decode())
|
versions = json.loads(client.get(url).content.decode())
|
||||||
assert len(versions["versions"]) == 4
|
assert len(versions["versions"]) == 4
|
||||||
version = {
|
version = {
|
||||||
|
@ -172,13 +220,25 @@ def test_versions_should_return_versions(client, datalayer, map, settings):
|
||||||
|
|
||||||
|
|
||||||
def test_version_should_return_one_version_geojson(client, datalayer, map):
|
def test_version_should_return_one_version_geojson(client, datalayer, map):
|
||||||
|
map.share_status = Map.PUBLIC
|
||||||
|
map.save()
|
||||||
root = datalayer.storage_root()
|
root = datalayer.storage_root()
|
||||||
name = "%s_1440924889.geojson" % datalayer.pk
|
name = "%s_1440924889.geojson" % datalayer.pk
|
||||||
datalayer.geojson.storage.save("%s/%s" % (root, name), ContentFile("{}"))
|
datalayer.geojson.storage.save("%s/%s" % (root, name), ContentFile("{}"))
|
||||||
url = reverse("datalayer_version", args=(datalayer.pk, name))
|
url = reverse("datalayer_version", args=(map.pk, datalayer.pk, name))
|
||||||
assert client.get(url).content.decode() == "{}"
|
assert client.get(url).content.decode() == "{}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_should_return_403_if_not_allowed(client, datalayer, map):
|
||||||
|
map.share_status = Map.PRIVATE
|
||||||
|
map.save()
|
||||||
|
root = datalayer.storage_root()
|
||||||
|
name = "%s_1440924889.geojson" % datalayer.pk
|
||||||
|
datalayer.geojson.storage.save("%s/%s" % (root, name), ContentFile("{}"))
|
||||||
|
url = reverse("datalayer_version", args=(map.pk, datalayer.pk, name))
|
||||||
|
assert client.get(url).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_update_readonly(client, datalayer, map, post_data, settings):
|
def test_update_readonly(client, datalayer, map, post_data, settings):
|
||||||
settings.UMAP_READONLY = True
|
settings.UMAP_READONLY = True
|
||||||
url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
|
url = reverse("datalayer_update", args=(map.pk, datalayer.pk))
|
||||||
|
|
|
@ -119,17 +119,17 @@ def test_delete(client, map, datalayer):
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_slug_should_redirect_to_canonical(client, map):
|
def test_wrong_slug_should_redirect_to_canonical(client, map):
|
||||||
url = reverse("map", kwargs={"pk": map.pk, "slug": "wrong-slug"})
|
url = reverse("map", kwargs={"map_id": map.pk, "slug": "wrong-slug"})
|
||||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
canonical = reverse("map", kwargs={"map_id": map.pk, "slug": map.slug})
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert response.status_code == 301
|
assert response.status_code == 301
|
||||||
assert response["Location"] == canonical
|
assert response["Location"] == canonical
|
||||||
|
|
||||||
|
|
||||||
def test_wrong_slug_should_redirect_with_query_string(client, map):
|
def test_wrong_slug_should_redirect_with_query_string(client, map):
|
||||||
url = reverse("map", kwargs={"pk": map.pk, "slug": "wrong-slug"})
|
url = reverse("map", kwargs={"map_id": map.pk, "slug": "wrong-slug"})
|
||||||
url = "{}?allowEdit=0".format(url)
|
url = "{}?allowEdit=0".format(url)
|
||||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
canonical = reverse("map", kwargs={"map_id": map.pk, "slug": map.slug})
|
||||||
canonical = "{}?allowEdit=0".format(canonical)
|
canonical = "{}?allowEdit=0".format(canonical)
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert response.status_code == 301
|
assert response.status_code == 301
|
||||||
|
@ -137,7 +137,7 @@ def test_wrong_slug_should_redirect_with_query_string(client, map):
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_consider_the_query_string_for_canonical_check(client, map):
|
def test_should_not_consider_the_query_string_for_canonical_check(client, map):
|
||||||
url = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
url = reverse("map", kwargs={"map_id": map.pk, "slug": map.slug})
|
||||||
url = "{}?allowEdit=0".format(url)
|
url = "{}?allowEdit=0".format(url)
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
@ -145,7 +145,7 @@ def test_should_not_consider_the_query_string_for_canonical_check(client, map):
|
||||||
|
|
||||||
def test_short_url_should_redirect_to_canonical(client, map):
|
def test_short_url_should_redirect_to_canonical(client, map):
|
||||||
url = reverse("map_short_url", kwargs={"pk": map.pk})
|
url = reverse("map_short_url", kwargs={"pk": map.pk})
|
||||||
canonical = reverse("map", kwargs={"pk": map.pk, "slug": map.slug})
|
canonical = reverse("map", kwargs={"map_id": map.pk, "slug": map.slug})
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
assert response.status_code == 301
|
assert response.status_code == 301
|
||||||
assert response["Location"] == canonical
|
assert response["Location"] == canonical
|
||||||
|
@ -390,7 +390,7 @@ def test_no_cookie_cant_delete(client, anonymap):
|
||||||
@pytest.mark.usefixtures("allow_anonymous")
|
@pytest.mark.usefixtures("allow_anonymous")
|
||||||
def test_anonymous_edit_url(cookieclient, anonymap):
|
def test_anonymous_edit_url(cookieclient, anonymap):
|
||||||
url = anonymap.get_anonymous_edit_url()
|
url = anonymap.get_anonymous_edit_url()
|
||||||
canonical = reverse("map", kwargs={"pk": anonymap.pk, "slug": anonymap.slug})
|
canonical = reverse("map", kwargs={"map_id": anonymap.pk, "slug": anonymap.slug})
|
||||||
response = cookieclient.get(url)
|
response = cookieclient.get(url)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert response["Location"] == canonical
|
assert response["Location"] == canonical
|
||||||
|
@ -403,7 +403,7 @@ def test_sha1_anonymous_edit_url(cookieclient, anonymap):
|
||||||
signer = Signer(algorithm="sha1")
|
signer = Signer(algorithm="sha1")
|
||||||
signature = signer.sign(anonymap.pk)
|
signature = signer.sign(anonymap.pk)
|
||||||
url = reverse("map_anonymous_edit_url", kwargs={"signature": signature})
|
url = reverse("map_anonymous_edit_url", kwargs={"signature": signature})
|
||||||
canonical = reverse("map", kwargs={"pk": anonymap.pk, "slug": anonymap.slug})
|
canonical = reverse("map", kwargs={"map_id": anonymap.pk, "slug": anonymap.slug})
|
||||||
response = cookieclient.get(url)
|
response = cookieclient.get(url)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert response["Location"] == canonical
|
assert response["Location"] == canonical
|
||||||
|
|
18
umap/urls.py
18
umap/urls.py
|
@ -14,6 +14,7 @@ from .decorators import (
|
||||||
jsonize_view,
|
jsonize_view,
|
||||||
login_required_if_not_anonymous_allowed,
|
login_required_if_not_anonymous_allowed,
|
||||||
map_permissions_check,
|
map_permissions_check,
|
||||||
|
can_view_map,
|
||||||
)
|
)
|
||||||
from .utils import decorated_patterns
|
from .utils import decorated_patterns
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ i18n_urls = [
|
||||||
),
|
),
|
||||||
re_path(r"^logout/$", views.logout, name="logout"),
|
re_path(r"^logout/$", views.logout, name="logout"),
|
||||||
re_path(
|
re_path(
|
||||||
r"^map/(?P<pk>\d+)/geojson/$",
|
r"^map/(?P<map_id>\d+)/geojson/$",
|
||||||
views.MapViewGeoJSON.as_view(),
|
views.MapViewGeoJSON.as_view(),
|
||||||
name="map_geojson",
|
name="map_geojson",
|
||||||
),
|
),
|
||||||
|
@ -63,28 +64,31 @@ i18n_urls = [
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
cache_control(must_revalidate=True),
|
[can_view_map, cache_control(must_revalidate=True)],
|
||||||
re_path(
|
re_path(
|
||||||
r"^datalayer/(?P<pk>[\d]+)/$",
|
r"^datalayer/(?P<map_id>\d+)/(?P<pk>[\d]+)/$",
|
||||||
views.DataLayerView.as_view(),
|
views.DataLayerView.as_view(),
|
||||||
name="datalayer_view",
|
name="datalayer_view",
|
||||||
),
|
),
|
||||||
re_path(
|
re_path(
|
||||||
r"^datalayer/(?P<pk>[\d]+)/versions/$",
|
r"^datalayer/(?P<map_id>\d+)/(?P<pk>[\d]+)/versions/$",
|
||||||
views.DataLayerVersions.as_view(),
|
views.DataLayerVersions.as_view(),
|
||||||
name="datalayer_versions",
|
name="datalayer_versions",
|
||||||
),
|
),
|
||||||
re_path(
|
re_path(
|
||||||
r"^datalayer/(?P<pk>[\d]+)/(?P<name>[_\w]+.geojson)$",
|
r"^datalayer/(?P<map_id>\d+)/(?P<pk>[\d]+)/(?P<name>[_\w]+.geojson)$",
|
||||||
views.DataLayerVersion.as_view(),
|
views.DataLayerVersion.as_view(),
|
||||||
name="datalayer_version",
|
name="datalayer_version",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
[ensure_csrf_cookie],
|
[can_view_map, ensure_csrf_cookie],
|
||||||
re_path(
|
re_path(
|
||||||
r"^map/(?P<slug>[-_\w]+)_(?P<pk>\d+)$", views.MapView.as_view(), name="map"
|
r"^map/(?P<slug>[-_\w]+)_(?P<map_id>\d+)$", views.MapView.as_view(), name="map"
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
i18n_urls += decorated_patterns(
|
||||||
|
[ensure_csrf_cookie],
|
||||||
re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"),
|
re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"),
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
|
|
|
@ -441,6 +441,7 @@ class FormLessEditMixin:
|
||||||
|
|
||||||
class MapDetailMixin:
|
class MapDetailMixin:
|
||||||
model = Map
|
model = Map
|
||||||
|
pk_url_kwarg = "map_id"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
@ -544,8 +545,6 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
|
||||||
if request.META.get("QUERY_STRING"):
|
if request.META.get("QUERY_STRING"):
|
||||||
canonical = "?".join([canonical, request.META["QUERY_STRING"]])
|
canonical = "?".join([canonical, request.META["QUERY_STRING"]])
|
||||||
return HttpResponsePermanentRedirect(canonical)
|
return HttpResponsePermanentRedirect(canonical)
|
||||||
if not self.object.can_view(request):
|
|
||||||
return HttpResponseForbidden()
|
|
||||||
return super(MapView, self).get(request, *args, **kwargs)
|
return super(MapView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_canonical_url(self):
|
def get_canonical_url(self):
|
||||||
|
|
Loading…
Reference in a new issue