mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-09-09 06:40:17 +00:00
This patch applies the upstream fix as referenced in [2], using the commit shown in [1]. [1] https://github.com/django/django/commit/115ffd0463a765ab1cc93de18e94b5459b8a300e [2] https://nvd.nist.gov/vuln/detail/CVE-2026-15307 Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
564 lines
24 KiB
Diff
564 lines
24 KiB
Diff
From 1776209a7609053b6afe6cb0f14960c478de5424 Mon Sep 17 00:00:00 2001
|
|
From: Jacob Walls <jacobtylerwalls@gmail.com>
|
|
Date: Thu, 9 Jul 2026 11:07:28 -0400
|
|
Subject: [PATCH] [5.2.x] Fixed CVE-2026-15307 -- Blocked raster strings and
|
|
dicts in spatial lookups.
|
|
|
|
Spatial lookups optimistically parse values as rasters before retrying
|
|
as geometries. If a malicious value reached the GDALRaster constructor,
|
|
depending on the raster driver, it might write to disk or fetch from the
|
|
network regardless of the constructor's `write=False` default argument.
|
|
|
|
Although this works as designed for model field assignment, this is
|
|
potentially unexpected for querying, for example, in the admin's
|
|
changelist view, which allows staff users to execute arbitrary lookups
|
|
on models registered with the admin.
|
|
|
|
Network rasters didn't even work in lookup contexts before, providing
|
|
further evidence that this use case was unintentional. (The failure
|
|
point was after the fetching, however.)
|
|
|
|
Now, strings and dicts representing rasters are rejected by spatial
|
|
lookups. To opt in to using them, wrap them in a `GDALRaster` first.
|
|
|
|
Although it would simplify the implementation to try geometries before
|
|
rasters (instead of stashing a raster exception and raising it later),
|
|
we maintain the current order, which has been stable for a decade.
|
|
|
|
Thanks Bence Nagy, localhost-detect, and kimchunbok_ for providing
|
|
information useful in evaluating this report. Thanks Simon Charette,
|
|
Natalia Bidart, and Sarah Boyce for reviews.
|
|
|
|
Backport of f1949c1f9758947ade984c895ff16bef46f56520 from main.
|
|
|
|
CVE: CVE-2026-15307
|
|
Upstream-Status: Backport [https://github.com/django/django/commit/115ffd0463a765ab1cc93de18e94b5459b8a300e]
|
|
|
|
Backport Changes:
|
|
- Dropped the file docs/releases/5.2.17.txt as
|
|
current version for Scarthgap is 5.0.14
|
|
|
|
(cherry picked from commit 115ffd0463a765ab1cc93de18e94b5459b8a300e)
|
|
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
|
|
---
|
|
django/contrib/gis/db/models/fields.py | 37 +++++---
|
|
django/contrib/gis/gdal/raster/source.py | 46 ++++++++--
|
|
docs/ref/contrib/gis/db-api.txt | 12 ++-
|
|
docs/ref/contrib/gis/gdal.txt | 34 +++++++
|
|
tests/gis_tests/geoadmin/tests.py | 25 ++++-
|
|
tests/gis_tests/geoapp/tests.py | 91 +++++++++++++++++++
|
|
tests/gis_tests/rasterapp/test_rasterfield.py | 25 ++---
|
|
tests/gis_tests/test_geoforms.py | 15 +++
|
|
8 files changed, 246 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py
|
|
index 889c1cfe84..15d9ae3c57 100644
|
|
--- a/django/contrib/gis/db/models/fields.py
|
|
+++ b/django/contrib/gis/db/models/fields.py
|
|
@@ -3,6 +3,9 @@ from collections import defaultdict, namedtuple
|
|
from django.contrib.gis import forms, gdal
|
|
from django.contrib.gis.db.models.proxy import SpatialProxy
|
|
from django.contrib.gis.gdal.error import GDALException
|
|
+from django.contrib.gis.gdal.raster.const import VSI_FILESYSTEM_PREFIX
|
|
+from django.contrib.gis.gdal.raster.source import DisallowedRasterLookup
|
|
+from django.contrib.gis.geometry import json_regex
|
|
from django.contrib.gis.geos import (
|
|
GeometryCollection,
|
|
GEOSException,
|
|
@@ -172,21 +175,19 @@ class BaseSpatialField(Field):
|
|
def get_raster_prep_value(self, value, is_candidate):
|
|
"""
|
|
Return a GDALRaster if conversion is successful, otherwise return None.
|
|
+
|
|
+ Unless the user opts in by wrapping values in a GDALRaster, raise
|
|
+ DisallowedRasterLookup for values that fetch or write to disk.
|
|
"""
|
|
if isinstance(value, gdal.GDALRaster):
|
|
return value
|
|
- elif is_candidate:
|
|
+ gdal.GDALRaster.check_raster_lookup_value(value)
|
|
+ if is_candidate:
|
|
try:
|
|
return gdal.GDALRaster(value)
|
|
except GDALException:
|
|
pass
|
|
- elif isinstance(value, dict):
|
|
- try:
|
|
- return gdal.GDALRaster(value)
|
|
- except GDALException:
|
|
- raise ValueError(
|
|
- "Couldn't create spatial object from lookup value '%s'." % value
|
|
- )
|
|
+ return None
|
|
|
|
def get_prep_value(self, value):
|
|
obj = super().get_prep_value(value)
|
|
@@ -202,22 +203,36 @@ class BaseSpatialField(Field):
|
|
obj, "__geo_interface__"
|
|
)
|
|
# Try to convert the input to raster.
|
|
- raster = self.get_raster_prep_value(obj, is_candidate)
|
|
-
|
|
+ raster = None
|
|
+ blocked_err = None
|
|
+ try:
|
|
+ raster = self.get_raster_prep_value(obj, is_candidate)
|
|
+ except DisallowedRasterLookup as err:
|
|
+ if isinstance(obj, dict):
|
|
+ raise err
|
|
+ # Don't immediately raise in case this is a valid GEOSGeometry.
|
|
+ blocked_err = err
|
|
if raster:
|
|
obj = raster
|
|
elif is_candidate:
|
|
try:
|
|
obj = GEOSGeometry(obj)
|
|
+ except (TypeError, ValueError) as err:
|
|
+ if isinstance(obj, str) and obj.startswith(VSI_FILESYSTEM_PREFIX):
|
|
+ raise blocked_err
|
|
+ raise err
|
|
except (GEOSException, GDALException):
|
|
+ if isinstance(obj, str) and json_regex.match(obj):
|
|
+ raise blocked_err
|
|
raise ValueError(
|
|
"Couldn't create spatial object from lookup value '%s'." % obj
|
|
)
|
|
else:
|
|
- raise ValueError(
|
|
+ msg = (
|
|
"Cannot use object with type %s for a spatial lookup parameter."
|
|
% type(obj).__name__
|
|
)
|
|
+ raise blocked_err or ValueError(msg)
|
|
|
|
# Assigning the SRID value.
|
|
obj.srid = self.get_srid(obj)
|
|
diff --git a/django/contrib/gis/gdal/raster/source.py b/django/contrib/gis/gdal/raster/source.py
|
|
index b33eb11c0f..f63e7d0f30 100644
|
|
--- a/django/contrib/gis/gdal/raster/source.py
|
|
+++ b/django/contrib/gis/gdal/raster/source.py
|
|
@@ -28,10 +28,19 @@ from django.contrib.gis.gdal.raster.const import (
|
|
)
|
|
from django.contrib.gis.gdal.srs import SpatialReference, SRSException
|
|
from django.contrib.gis.geometry import json_regex
|
|
+from django.core.exceptions import SuspiciousOperation
|
|
from django.utils.encoding import force_bytes, force_str
|
|
from django.utils.functional import cached_property
|
|
|
|
|
|
+class DisallowedRasterLookup(SuspiciousOperation):
|
|
+ """
|
|
+ Types that force GDALRaster to open in write mode (dict) or values that
|
|
+ could be virtual filesystem paths (str) are not allowed in lookup contexts.
|
|
+ Instead, wrap values in GDALRaster explicitly.
|
|
+ """
|
|
+
|
|
+
|
|
class TransformPoint(list):
|
|
indices = {
|
|
"origin": (0, 3),
|
|
@@ -78,14 +87,10 @@ class GDALRaster(GDALRasterBase):
|
|
self._write = 1 if write else 0
|
|
Driver.ensure_registered()
|
|
|
|
- # Preprocess json inputs. This converts json strings to dictionaries,
|
|
- # which are parsed below the same way as direct dictionary inputs.
|
|
- if isinstance(ds_input, str) and json_regex.match(ds_input):
|
|
- ds_input = json.loads(ds_input)
|
|
+ ds_input = self._preprocess_input(ds_input)
|
|
|
|
# If input is a valid file path, try setting file as source.
|
|
- if isinstance(ds_input, (str, Path)):
|
|
- ds_input = str(ds_input)
|
|
+ if isinstance(ds_input, str):
|
|
if not ds_input.startswith(VSI_FILESYSTEM_PREFIX) and not os.path.exists(
|
|
ds_input
|
|
):
|
|
@@ -226,6 +231,35 @@ class GDALRaster(GDALRasterBase):
|
|
"""
|
|
return "<Raster object at %s>" % hex(addressof(self._ptr))
|
|
|
|
+ @classmethod
|
|
+ def _preprocess_input(cls, ds_input):
|
|
+ """
|
|
+ Preprocess json and Path inputs. This converts json strings to
|
|
+ dictionaries, which are then parsed just like direct dictionary inputs.
|
|
+ This also stringifies Path objects.
|
|
+ """
|
|
+ if isinstance(ds_input, str) and json_regex.match(ds_input):
|
|
+ ds_input = json.loads(ds_input)
|
|
+ if isinstance(ds_input, Path):
|
|
+ ds_input = str(ds_input)
|
|
+ return ds_input
|
|
+
|
|
+ @classmethod
|
|
+ def check_raster_lookup_value(cls, ds_input):
|
|
+ """
|
|
+ Raise DisallowedRasterLookup for values inappropriate in lookups:
|
|
+ - No dicts, which GDALRaster(write=False) might still write to.
|
|
+ - No strings or Paths, which might fetch over the virtual filesystem.
|
|
+ """
|
|
+ normalized = cls._preprocess_input(ds_input)
|
|
+ if isinstance(normalized, (dict, str)):
|
|
+ msg = (
|
|
+ f"Cannot use object {normalized!r} for a spatial lookup "
|
|
+ "parameter. If this is a raster, wrap it with GDALRaster() "
|
|
+ "before using it in a lookup to enable writing or fetching."
|
|
+ )
|
|
+ raise DisallowedRasterLookup(msg)
|
|
+
|
|
def _flush(self):
|
|
"""
|
|
Flush all data from memory into the source file if it exists.
|
|
diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt
|
|
index df1d3847e6..51dece9b63 100644
|
|
--- a/docs/ref/contrib/gis/db-api.txt
|
|
+++ b/docs/ref/contrib/gis/db-api.txt
|
|
@@ -146,11 +146,21 @@ GeoDjango are only available on spatial fields.
|
|
|
|
Filters on 'normal' fields (e.g. :class:`~django.db.models.CharField`)
|
|
may be chained with those on geographic fields. Geographic lookups accept
|
|
-geometry and raster input on both sides and input types can be mixed freely.
|
|
+geometry and raster input on both sides, and input types can be mixed freely in
|
|
+most cases. However, unlike assignments to model fields, with lookups,
|
|
+types such as ``str``, :class:`pathlib.Path`, and ``dict`` must be wrapped by
|
|
+:class:`~django.contrib.gis.gdal.GDALRaster` to signify that the potential for
|
|
+file writing or network fetching is acceptable. For the rationale, see
|
|
+:ref:`raster security considerations <raster-security>`.
|
|
|
|
The general structure of geographic lookups is described below. A complete
|
|
reference can be found in the :ref:`spatial lookup reference<spatial-lookups>`.
|
|
|
|
+.. versionchanged:: 5.2.17
|
|
+
|
|
+ In earlier versions, spatial lookups accepted ``str`` and ``dict`` types
|
|
+ for new rasters, allowing file writes and network fetches.
|
|
+
|
|
Geometry Lookups
|
|
----------------
|
|
|
|
diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt
|
|
index 9011aa6e2b..2df807ee74 100644
|
|
--- a/docs/ref/contrib/gis/gdal.txt
|
|
+++ b/docs/ref/contrib/gis/gdal.txt
|
|
@@ -2068,6 +2068,40 @@ previously configured for authentication and possibly other settings (see the
|
|
|
|
.. _`GDAL Virtual Filesystems documentation`: https://gdal.org/user/virtual_file_systems.html
|
|
|
|
+.. _raster-security:
|
|
+
|
|
+Security considerations
|
|
+~~~~~~~~~~~~~~~~~~~~~~~
|
|
+
|
|
+Since :class:`GDALRaster` always opens new rasters in write mode, it is
|
|
+essential to prevent instantiating one from untrusted input. Otherwise, an
|
|
+attacker might gain the ability to write a file or make a network request.
|
|
+
|
|
+To mitigate this, :ref:`spatial lookups <spatial-lookups-intro>` prevent
|
|
+``str``, :class:`pathlib.Path`, and ``dict`` values from reaching
|
|
+:class:`GDALRaster` altogether. To use these types with lookups, wrap them
|
|
+explicitly with :class:`GDALRaster`, indicating that the value is trusted.
|
|
+Bytes are accepted without being wrapped in :class:`GDALRaster` because they
|
|
+are opened through GDAL's memory-based :ref:`virtual filesystem
|
|
+<gdal-raster-vsimem>`.
|
|
+
|
|
+This protection applies only to spatial lookups. Assigning a ``dict`` value to
|
|
+a :class:`~django.contrib.gis.db.models.RasterField` will still open a new
|
|
+raster, and assigning a ``str`` or ``Path`` will still fetch and open the
|
|
+referenced raster.
|
|
+
|
|
+When validating geometry inputs, the
|
|
+:class:`~django.contrib.gis.forms.GeometryField` form field will reject raster
|
|
+values. When validating raster inputs, you should write custom validation.
|
|
+
|
|
+For defense-in-depth strategies for limiting the available raster drivers, see
|
|
+`GDAL security considerations <https://gdal.org/user/security.html>`_.
|
|
+
|
|
+.. versionchanged:: 5.2.17
|
|
+
|
|
+ In earlier versions, spatial lookups accepted ``str`` and ``dict`` types
|
|
+ for new rasters, allowing file writes and network fetches.
|
|
+
|
|
Settings
|
|
========
|
|
|
|
diff --git a/tests/gis_tests/geoadmin/tests.py b/tests/gis_tests/geoadmin/tests.py
|
|
index e101050464..2db36b49de 100644
|
|
--- a/tests/gis_tests/geoadmin/tests.py
|
|
+++ b/tests/gis_tests/geoadmin/tests.py
|
|
@@ -1,13 +1,26 @@
|
|
+from django.contrib.auth.models import Permission, User
|
|
+from django.contrib.contenttypes.models import ContentType
|
|
from django.contrib.gis.geos import Point
|
|
-from django.test import SimpleTestCase, override_settings
|
|
+from django.core.exceptions import SuspiciousOperation
|
|
+from django.test import RequestFactory, TestCase, override_settings
|
|
|
|
from .models import City, site, site_gis, site_gis_custom
|
|
|
|
|
|
@override_settings(ROOT_URLCONF="django.contrib.gis.tests.geoadmin.urls")
|
|
-class GeoAdminTest(SimpleTestCase):
|
|
+class GeoAdminTest(TestCase):
|
|
admin_site = site # ModelAdmin
|
|
|
|
+ @classmethod
|
|
+ def setUpTestData(cls):
|
|
+ cls.user = User.objects.create_user("test", password="password", is_staff=True)
|
|
+ cls.user.user_permissions.add(
|
|
+ Permission.objects.get(
|
|
+ codename="view_city",
|
|
+ content_type=ContentType.objects.get_for_model(City),
|
|
+ )
|
|
+ )
|
|
+
|
|
def test_widget_empty_string(self):
|
|
geoadmin = self.admin_site.get_model_admin(City)
|
|
form = geoadmin.get_changelist_form(None)({"point": ""})
|
|
@@ -54,6 +67,14 @@ class GeoAdminTest(SimpleTestCase):
|
|
self.assertIs(has_changed(initial, data_almost_same), False)
|
|
self.assertIs(has_changed(initial, data_changed), True)
|
|
|
|
+ def test_raster_lookup_not_allowed(self):
|
|
+ geoadmin = self.admin_site.get_model_admin(City)
|
|
+ request = RequestFactory().get("/city/", data={"point": "/vsicurl/someurl"})
|
|
+ request.user = self.user
|
|
+ msg = "Cannot use object '/vsicurl/someurl' for a spatial lookup parameter."
|
|
+ with self.assertRaisesMessage(SuspiciousOperation, msg):
|
|
+ geoadmin.get_changelist_instance(request)
|
|
+
|
|
|
|
class GISAdminTests(GeoAdminTest):
|
|
admin_site = site_gis # GISModelAdmin
|
|
diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py
|
|
index 7ee47ee9a8..6be13d4907 100644
|
|
--- a/tests/gis_tests/geoapp/tests.py
|
|
+++ b/tests/gis_tests/geoapp/tests.py
|
|
@@ -1,7 +1,10 @@
|
|
+import json
|
|
from io import StringIO
|
|
+from pathlib import Path
|
|
|
|
from django.contrib.gis import gdal
|
|
from django.contrib.gis.db.models import Extent, MakeLine, Union, functions
|
|
+from django.contrib.gis.gdal.raster.source import DisallowedRasterLookup
|
|
from django.contrib.gis.geos import (
|
|
GeometryCollection,
|
|
GEOSGeometry,
|
|
@@ -21,6 +24,7 @@ from django.db.models import F, OuterRef, Subquery
|
|
from django.test import TestCase, skipUnlessDBFeature
|
|
from django.test.utils import CaptureQueriesContext
|
|
|
|
+from ..data.rasters.textrasters import JSON_RASTER
|
|
from ..utils import skipUnlessGISLookup
|
|
from .models import (
|
|
City,
|
|
@@ -598,6 +602,93 @@ class GeoLookupTest(TestCase):
|
|
)
|
|
self.assertEqual(qs.get(), multifields)
|
|
|
|
+ def test_lookup_rejects_writing_or_fetching_rasters(self):
|
|
+ """
|
|
+ GDALRaster enables write mode in the following cases even when the
|
|
+ value of the `write` parameter is False (default):
|
|
+ - dicts
|
|
+ - strings matching a json regex
|
|
+ - bytes
|
|
+
|
|
+ Since this could be unexpected in a lookup context, disallow dicts
|
|
+ and strings: instead, explicitly wrap with GDALRaster() to signal that
|
|
+ a write or fetch is expected. Bytes only write to the in-memory virtual
|
|
+ filesystem, so allow them.
|
|
+
|
|
+ Disallowing strings also disallows paths to local or network rasters,
|
|
+ but those didn't work in the lookup context anyway, since they were
|
|
+ never opened for writing, and lookups failed on setting the SRID with:
|
|
+
|
|
+ GDALException: Raster needs to be opened in write mode to change values
|
|
+
|
|
+ Still, a network fetch might have occurred before that failure point,
|
|
+ so disallow strings altogether.
|
|
+ """
|
|
+ # Create a vsi-based raster from scratch.
|
|
+ vsimem_path = "/vsimem/raster.tif"
|
|
+ # Keep a reference to this raster while it is being re-parsed below.
|
|
+ # Otherwise, GDALRaster.__del__() will delete the in-memory raster.
|
|
+ _rast = gdal.GDALRaster( # NOQA: F841
|
|
+ {
|
|
+ "name": vsimem_path,
|
|
+ "driver": "tif",
|
|
+ "width": 4,
|
|
+ "height": 4,
|
|
+ "srid": 4326,
|
|
+ "bands": [
|
|
+ {
|
|
+ "data": range(16),
|
|
+ }
|
|
+ ],
|
|
+ }
|
|
+ )
|
|
+ existing_path = Path(__file__).parent.parent / "data" / "rasters" / "raster.tif"
|
|
+ disallowed_cases = [
|
|
+ JSON_RASTER,
|
|
+ json.loads(JSON_RASTER),
|
|
+ "/vsicurl/someurl",
|
|
+ "/vsicurl_streaming/someurl",
|
|
+ "/vsis3/someurl",
|
|
+ vsimem_path,
|
|
+ existing_path,
|
|
+ ]
|
|
+ for obj in disallowed_cases:
|
|
+ try:
|
|
+ msg_obj = json.loads(obj)
|
|
+ except Exception:
|
|
+ if isinstance(obj, Path):
|
|
+ msg_obj = str(obj)
|
|
+ else:
|
|
+ msg_obj = obj
|
|
+ msg = (
|
|
+ f"Cannot use object {msg_obj!r} for a spatial lookup parameter. "
|
|
+ "If this is a raster, wrap it with GDALRaster() before using "
|
|
+ "it in a lookup to enable writing or fetching."
|
|
+ )
|
|
+ with (
|
|
+ self.subTest(obj=obj),
|
|
+ self.assertRaisesMessage(DisallowedRasterLookup, msg),
|
|
+ ):
|
|
+ City.objects.filter(point__contained=obj)
|
|
+
|
|
+ # Strings having nothing to do with rasters raise a more generic error.
|
|
+ for obj in str(existing_path), "invalid":
|
|
+ msg = "String input unrecognized as WKT EWKT, and HEXEWKB."
|
|
+ with self.subTest(obj=obj), self.assertRaisesMessage(ValueError, msg):
|
|
+ City.objects.filter(point__contained=obj)
|
|
+
|
|
+ def test_lookup_allows_writing_raster_from_bytes(self):
|
|
+ raster_path = Path(__file__).parent.parent / "data" / "rasters" / "raster.tif"
|
|
+ with open(raster_path, "rb") as raster_file:
|
|
+ raster_bytes = raster_file.read()
|
|
+ # Just get SQL to avoid gating on connection.supports_raster.
|
|
+ City.objects.filter(point__contained=raster_bytes).query
|
|
+
|
|
+ def test_lookup_allows_geos_geometry_string(self):
|
|
+ geojson = json.dumps({"type": "Point", "coordinates": [2, 49]})
|
|
+ # Just get SQL to avoid gating on connection.supports_raster.
|
|
+ City.objects.filter(point__contained=geojson).query
|
|
+
|
|
|
|
class GeoQuerySetTest(TestCase):
|
|
# TODO: GeoQuerySet is removed, organize these test better.
|
|
diff --git a/tests/gis_tests/rasterapp/test_rasterfield.py b/tests/gis_tests/rasterapp/test_rasterfield.py
|
|
index 3f2ce770a9..37eec50027 100644
|
|
--- a/tests/gis_tests/rasterapp/test_rasterfield.py
|
|
+++ b/tests/gis_tests/rasterapp/test_rasterfield.py
|
|
@@ -207,7 +207,7 @@ class RasterFieldTest(TransactionTestCase):
|
|
(stx_pnt, 0, 500),
|
|
(stx_pnt, D(km=1000)),
|
|
(rast, 500),
|
|
- (json.loads(JSON_RASTER), 500),
|
|
+ (GDALRaster(json.loads(JSON_RASTER)), 500),
|
|
]
|
|
elif name == "relate":
|
|
# Set lookup values for the relate lookup.
|
|
@@ -218,7 +218,7 @@ class RasterFieldTest(TransactionTestCase):
|
|
(stx_pnt, 0, "T*T***FF*"),
|
|
(stx_pnt, "T*T***FF*"),
|
|
(rast, "T*T***FF*"),
|
|
- (json.loads(JSON_RASTER), "T*T***FF*"),
|
|
+ (GDALRaster(json.loads(JSON_RASTER)), "T*T***FF*"),
|
|
]
|
|
elif name == "isvalid":
|
|
# The isvalid lookup doesn't make sense for rasters.
|
|
@@ -232,7 +232,7 @@ class RasterFieldTest(TransactionTestCase):
|
|
(stx_pnt, 0),
|
|
stx_pnt,
|
|
rast,
|
|
- json.loads(JSON_RASTER),
|
|
+ GDALRaster(json.loads(JSON_RASTER)),
|
|
]
|
|
else:
|
|
# Override band lookup for these, as it's not supported.
|
|
@@ -245,7 +245,7 @@ class RasterFieldTest(TransactionTestCase):
|
|
stx_pnt,
|
|
stx_pnt,
|
|
rast,
|
|
- json.loads(JSON_RASTER),
|
|
+ GDALRaster(json.loads(JSON_RASTER)),
|
|
]
|
|
|
|
# Create query filter combinations.
|
|
@@ -287,14 +287,6 @@ class RasterFieldTest(TransactionTestCase):
|
|
qs = RasterModel.objects.filter(rastprojected__dwithin=(rast, D(km=1)))
|
|
self.assertEqual(qs.count(), 1)
|
|
|
|
- qs = RasterModel.objects.filter(
|
|
- rastprojected__dwithin=(json.loads(JSON_RASTER), D(km=1))
|
|
- )
|
|
- self.assertEqual(qs.count(), 1)
|
|
-
|
|
- qs = RasterModel.objects.filter(rastprojected__dwithin=(JSON_RASTER, D(km=1)))
|
|
- self.assertEqual(qs.count(), 1)
|
|
-
|
|
# Filter in an unprojected coordinate system.
|
|
qs = RasterModel.objects.filter(rast__dwithin=(rast, 40))
|
|
self.assertEqual(qs.count(), 1)
|
|
@@ -414,13 +406,8 @@ class RasterFieldTest(TransactionTestCase):
|
|
self.assertEqual(qs.count(), 0)
|
|
|
|
def test_lookup_value_error(self):
|
|
- # Test with invalid dict lookup parameter
|
|
- obj = {}
|
|
- msg = "Couldn't create spatial object from lookup value '%s'." % obj
|
|
- with self.assertRaisesMessage(ValueError, msg):
|
|
- RasterModel.objects.filter(geom__intersects=obj)
|
|
# Test with invalid string lookup parameter
|
|
- obj = "00000"
|
|
+ obj = "POINT()"
|
|
msg = "Couldn't create spatial object from lookup value '%s'." % obj
|
|
with self.assertRaisesMessage(ValueError, msg):
|
|
RasterModel.objects.filter(geom__intersects=obj)
|
|
@@ -449,7 +436,7 @@ class RasterFieldTest(TransactionTestCase):
|
|
def test_lhs_with_index_rhs_without_index(self):
|
|
with CaptureQueriesContext(connection) as queries:
|
|
RasterModel.objects.filter(
|
|
- rast__0__contains=json.loads(JSON_RASTER)
|
|
+ rast__0__contains=GDALRaster(json.loads(JSON_RASTER))
|
|
).exists()
|
|
# It's easier to check the indexes in the generated SQL than to write
|
|
# tests that cover all index combinations.
|
|
diff --git a/tests/gis_tests/test_geoforms.py b/tests/gis_tests/test_geoforms.py
|
|
index b8105645bf..b980892790 100644
|
|
--- a/tests/gis_tests/test_geoforms.py
|
|
+++ b/tests/gis_tests/test_geoforms.py
|
|
@@ -8,6 +8,8 @@ from django.test import SimpleTestCase, override_settings
|
|
from django.utils.deprecation import RemovedInDjango51Warning
|
|
from django.utils.html import escape
|
|
|
|
+from .data.rasters.textrasters import JSON_RASTER
|
|
+
|
|
|
|
class GeometryFieldTest(SimpleTestCase):
|
|
def test_init(self):
|
|
@@ -82,6 +84,19 @@ class GeometryFieldTest(SimpleTestCase):
|
|
with self.assertRaises(ValidationError):
|
|
pnt_fld.clean("LINESTRING(0 0, 1 1)")
|
|
|
|
+ def test_raster_types(self):
|
|
+ fld = forms.GeometryField()
|
|
+ for value in (
|
|
+ JSON_RASTER,
|
|
+ str(JSON_RASTER),
|
|
+ "/vsicurl/http://example.com/raster.tif",
|
|
+ ):
|
|
+ with (
|
|
+ self.subTest(value=value),
|
|
+ self.assertRaisesMessage(ValidationError, "Invalid geometry value."),
|
|
+ ):
|
|
+ fld.clean(value)
|
|
+
|
|
def test_to_python(self):
|
|
"""
|
|
to_python() either returns a correct GEOSGeometry object or
|
|
--
|
|
2.44.4
|