From 89ae02d60a5a2888a4eb65d894e339ca13609463 Mon Sep 17 00:00:00 2001 From: sijandh35 Date: Mon, 15 Dec 2025 10:03:33 +0000 Subject: [PATCH 1/3] [Fixes #13826] thumbnail issue on map page fix --- geonode/thumbs/thumbnails.py | 21 +++++++++++++++++++-- geonode/thumbs/utils.py | 19 +++++++++---------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/geonode/thumbs/thumbnails.py b/geonode/thumbs/thumbnails.py index a54679b61bd..9c4386f0b3d 100644 --- a/geonode/thumbs/thumbnails.py +++ b/geonode/thumbs/thumbnails.py @@ -143,7 +143,7 @@ def create_thumbnail( # --- fetch WMS datasets --- partial_thumbs = [] - for ogc_server, datasets, _styles in locations: + for ogc_server, datasets, _styles, auth_info in locations: if isinstance(instance, Map): styles = [] if len(datasets) == len(_styles): @@ -160,6 +160,7 @@ def create_thumbnail( width=width, height=height, instance=instance, + auth_info=auth_info, ) ) except Exception as e: @@ -270,7 +271,14 @@ def _datasets_locations( locations = [] bbox = [] if isinstance(instance, Dataset): - locations.append([instance.ows_url or ogc_server_settings.LOCATION, [instance.alternate], []]) + # Check if dataset has remote service with authentication + auth_info = {} + if instance.remote_service and instance.remote_service.needs_authentication: + auth_info = { + 'username': instance.remote_service.username, + 'password': instance.remote_service.get_password() + } + locations.append([instance.ows_url or ogc_server_settings.LOCATION, [instance.alternate], [], auth_info]) if compute_bbox: if instance.ll_bbox_polygon: bbox = bbox_utils.clean_bbox(instance.ll_bbox, target_crs) @@ -308,6 +316,13 @@ def _datasets_locations( continue if dataset.subtype in ["tileStore", "remote"]: + # Check if remote service requires authentication + auth_info = {} + if dataset.remote_service and dataset.remote_service.needs_authentication: + auth_info = { + 'username': dataset.remote_service.username, + 'password': dataset.remote_service.get_password() + } # limit number of locations, ensuring dataset order if len(locations) and locations[-1][0] == dataset.remote_service.service_url: # if previous dataset's location is the same as the current one - append current dataset there @@ -321,6 +336,7 @@ def _datasets_locations( dataset.remote_service.service_url, [dataset.alternate], [map_dataset_style] if map_dataset_style else [], + auth_info, ] ) else: @@ -337,6 +353,7 @@ def _datasets_locations( settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], [map_dataset_style] if map_dataset_style else [], + {}, ] ) diff --git a/geonode/thumbs/utils.py b/geonode/thumbs/utils.py index 9bd4b6f3ec7..aa72f105256 100644 --- a/geonode/thumbs/utils.py +++ b/geonode/thumbs/utils.py @@ -145,6 +145,7 @@ def get_map( max_retries: int = 3, retry_delay: int = 1, instance=None, + auth_info: dict = None, ): """ Function fetching an image from OGC server. @@ -164,6 +165,7 @@ def get_map( :param height: height of the returned image :param max_retries: maximum number of retries before skipping retrieval :param retry_delay: number of seconds waited between retries + :param auth_info: optional dict with 'username' and 'password' for remote service authentication :returns: retrieved image """ from geonode.geoserver.helpers import ogc_server_settings @@ -201,16 +203,13 @@ def get_map( else: headers["Authorization"] = f"Bearer {additional_kwargs['access_token']}" - if instance and instance.subtype == "remote" and instance.remote_typename: - from geonode.services.models import Service - - service = Service.objects.filter(name=instance.remote_typename, username__isnull=False, password__isnull=False) - if service.exists(): - service = service.first() - encoded_credentials = base64.b64encode( - f"{service.username}:{service.get_password()}".encode("UTF-8") - ).decode("ascii") - headers["Authorization"] = f"Basic {encoded_credentials}" + # Check if auth_info is provided (for remote services with authentication) + if auth_info and auth_info.get('username') and auth_info.get('password'): + # Use provided authentication credentials for remote service + encoded_credentials = base64.b64encode( + f"{auth_info['username']}:{auth_info['password']}".encode("UTF-8") + ).decode("ascii") + headers["Authorization"] = f"Basic {encoded_credentials}" image = None for retry in range(max_retries): From c0599afbb23818de74a97e230f26d856ea2524a4 Mon Sep 17 00:00:00 2001 From: sijandh35 Date: Mon, 15 Dec 2025 10:27:05 +0000 Subject: [PATCH 2/3] [Fixes #13826] updated older test cases and formated the code --- geonode/thumbs/tests/test_unit.py | 10 ++++++---- geonode/thumbs/thumbnails.py | 8 ++++---- geonode/thumbs/utils.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/geonode/thumbs/tests/test_unit.py b/geonode/thumbs/tests/test_unit.py index 8a311cd1b66..170d5283d5b 100644 --- a/geonode/thumbs/tests/test_unit.py +++ b/geonode/thumbs/tests/test_unit.py @@ -201,7 +201,7 @@ def test_datasets_locations_dataset(self): locations, bbox = thumbnails._datasets_locations(dataset) self.assertFalse(bbox, "Expected BBOX not to be calculated") - self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], []]]) + self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], [], {}]]) def test_datasets_locations_dataset_default_bbox(self): expected_bbox = [-8238681.374829309, -8220320.783295829, 4969844.0930337105, 4984363.884452854, "EPSG:3857"] @@ -211,7 +211,7 @@ def test_datasets_locations_dataset_default_bbox(self): self.assertEqual(bbox[-1].upper(), "EPSG:3857", "Expected calculated BBOX CRS to be EPSG:3857") self.assertEqual(bbox, expected_bbox, "Expected calculated BBOX to match pre-converted one.") - self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], []]]) + self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], [], {}]]) def test_datasets_locations_dataset_bbox(self): dataset = Dataset.objects.get(title="theaters_nyc") @@ -222,7 +222,7 @@ def test_datasets_locations_dataset_bbox(self): self.assertEqual( bbox[-1].lower(), dataset.bbox[-1].lower(), "Expected calculated BBOX's CRS to match dataset's" ) - self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], []]]) + self.assertEqual(locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], [], {}]]) def test_datasets_locations_simple_map(self): dataset = Dataset.objects.get(title="theaters_nyc") @@ -243,6 +243,7 @@ def test_datasets_locations_simple_map(self): settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate, "geonode:Meteorite_Landings_from_NASA_Open_Data_Portal1"], ["theaters_nyc", "test_style"], + {}, ] ], ) @@ -258,7 +259,7 @@ def test_datasets_locations_simple_map_default_bbox(self): self.assertEqual(bbox[-1].upper(), "EPSG:3857", "Expected calculated BBOX CRS to be EPSG:3857") self.assertEqual(bbox, expected_bbox, "Expected calculated BBOX to match pre-converted one.") self.assertEqual( - locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], ["theaters_nyc"]]] + locations, [[settings.OGC_SERVER["default"]["LOCATION"], [dataset.alternate], ["theaters_nyc"], {}]] ) def test_datasets_locations_composition_map_default_bbox(self): @@ -271,6 +272,7 @@ def test_datasets_locations_composition_map_default_bbox(self): "rt_geologia.dbg_risorse_minerarie", ], [], + {}, ] ] diff --git a/geonode/thumbs/thumbnails.py b/geonode/thumbs/thumbnails.py index 9c4386f0b3d..a28dd5a9d32 100644 --- a/geonode/thumbs/thumbnails.py +++ b/geonode/thumbs/thumbnails.py @@ -275,8 +275,8 @@ def _datasets_locations( auth_info = {} if instance.remote_service and instance.remote_service.needs_authentication: auth_info = { - 'username': instance.remote_service.username, - 'password': instance.remote_service.get_password() + "username": instance.remote_service.username, + "password": instance.remote_service.get_password(), } locations.append([instance.ows_url or ogc_server_settings.LOCATION, [instance.alternate], [], auth_info]) if compute_bbox: @@ -320,8 +320,8 @@ def _datasets_locations( auth_info = {} if dataset.remote_service and dataset.remote_service.needs_authentication: auth_info = { - 'username': dataset.remote_service.username, - 'password': dataset.remote_service.get_password() + "username": dataset.remote_service.username, + "password": dataset.remote_service.get_password(), } # limit number of locations, ensuring dataset order if len(locations) and locations[-1][0] == dataset.remote_service.service_url: diff --git a/geonode/thumbs/utils.py b/geonode/thumbs/utils.py index aa72f105256..8525902a434 100644 --- a/geonode/thumbs/utils.py +++ b/geonode/thumbs/utils.py @@ -204,7 +204,7 @@ def get_map( headers["Authorization"] = f"Bearer {additional_kwargs['access_token']}" # Check if auth_info is provided (for remote services with authentication) - if auth_info and auth_info.get('username') and auth_info.get('password'): + if auth_info and auth_info.get("username") and auth_info.get("password"): # Use provided authentication credentials for remote service encoded_credentials = base64.b64encode( f"{auth_info['username']}:{auth_info['password']}".encode("UTF-8") From 72329d107bdbe0b14a0347856c26c85f5ee0943b Mon Sep 17 00:00:00 2001 From: sijandh35 Date: Fri, 19 Dec 2025 06:31:22 +0000 Subject: [PATCH 3/3] [Fixes #13826] make remote service helper function common --- geonode/thumbs/thumbnails.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/geonode/thumbs/thumbnails.py b/geonode/thumbs/thumbnails.py index a28dd5a9d32..9197b36a4c6 100644 --- a/geonode/thumbs/thumbnails.py +++ b/geonode/thumbs/thumbnails.py @@ -249,6 +249,17 @@ def _generate_thumbnail_name(instance: Union[Dataset, Map, Document, GeoApp, Res return file_name +def _get_auth_info(dataset: "Dataset") -> dict: + """Gets authentication info for a dataset if it's a remote service requiring auth.""" + auth_info = {} + if dataset.remote_service and dataset.remote_service.needs_authentication: + auth_info = { + "username": dataset.remote_service.username, + "password": dataset.remote_service.get_password(), + } + return auth_info + + def _datasets_locations( instance: Union[Dataset, Map], compute_bbox: bool = False, target_crs: str = "EPSG:3857" ) -> Tuple[List[List], List]: @@ -272,12 +283,7 @@ def _datasets_locations( bbox = [] if isinstance(instance, Dataset): # Check if dataset has remote service with authentication - auth_info = {} - if instance.remote_service and instance.remote_service.needs_authentication: - auth_info = { - "username": instance.remote_service.username, - "password": instance.remote_service.get_password(), - } + auth_info = _get_auth_info(instance) locations.append([instance.ows_url or ogc_server_settings.LOCATION, [instance.alternate], [], auth_info]) if compute_bbox: if instance.ll_bbox_polygon: @@ -317,12 +323,7 @@ def _datasets_locations( if dataset.subtype in ["tileStore", "remote"]: # Check if remote service requires authentication - auth_info = {} - if dataset.remote_service and dataset.remote_service.needs_authentication: - auth_info = { - "username": dataset.remote_service.username, - "password": dataset.remote_service.get_password(), - } + auth_info = _get_auth_info(dataset) # limit number of locations, ensuring dataset order if len(locations) and locations[-1][0] == dataset.remote_service.service_url: # if previous dataset's location is the same as the current one - append current dataset there