[Git][debian-gis-team/pygeoapi][upstream] 2 commits: New upstream version 0.23.3
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Fri Jun 26 12:04:44 BST 2026
Bas Couwenberg pushed to branch upstream at Debian GIS Project / pygeoapi
Commits:
ee46986e by Bas Couwenberg at 2026-06-26T12:14:04+02:00
New upstream version 0.23.3
- - - - -
34393eae by Bas Couwenberg at 2026-06-26T12:14:54+02:00
New upstream version 0.23.4
- - - - -
11 changed files:
- docs/source/conf.py
- docs/source/configuration.rst
- pygeoapi/__init__.py
- pygeoapi/process/base.py
- pygeoapi/process/manager/base.py
- pygeoapi/provider/filesystem.py
- pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml
- pygeoapi/util.py
- requirements.txt
- tests/other/test_util.py
- tests/provider/test_filesystem_provider.py
Changes:
=====================================
docs/source/conf.py
=====================================
@@ -112,7 +112,7 @@ today_fmt = '%Y-%m-%d'
# built documents.
#
# The short X.Y version.
-version = '0.23.2'
+version = '0.23.4'
# The full version, including alpha/beta/rc tags.
release = version
=====================================
docs/source/configuration.rst
=====================================
@@ -292,6 +292,10 @@ default.
type: process # REQUIRED (collection, process, or stac-collection)
processor:
name: HelloWorld # Python path of process definition
+ # optional, allow for internal HTTP request execution
+ # if set to True, enables requests to link local ranges and loopback
+ # default: False
+ allow_internal_requests: True
.. seealso::
=====================================
pygeoapi/__init__.py
=====================================
@@ -30,7 +30,7 @@
#
# =================================================================
-__version__ = '0.23.2'
+__version__ = '0.23.4'
import click
try:
=====================================
pygeoapi/process/base.py
=====================================
@@ -3,7 +3,7 @@
# Authors: Tom Kralidis <tomkralidis at gmail.com>
# Francesco Martinelli <francesco.martinelli at ingv.it>
#
-# Copyright (c) 2022 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
# Copyright (c) 2024 Francesco Martinelli
#
# Permission is hereby granted, free of charge, to any person
@@ -53,6 +53,8 @@ class BaseProcessor:
self.name = processor_def['name']
self.metadata = process_metadata
self.supports_outputs = False
+ self.allow_internal_requests = processor_def.get(
+ 'allow_internal_requests', False)
def set_job_id(self, job_id: str) -> None:
"""
=====================================
pygeoapi/process/manager/base.py
=====================================
@@ -46,10 +46,12 @@ from pygeoapi.process.base import (
BaseProcessor,
JobNotFoundError,
JobResultNotFoundError,
+ ProcessorExecuteError,
UnknownProcessError,
)
from pygeoapi.util import (
get_current_datetime,
+ is_request_allowed,
JobStatus,
ProcessExecutionMode,
RequestedProcessExecutionMode,
@@ -105,7 +107,11 @@ class BaseManager:
except KeyError as err:
raise UnknownProcessError('Invalid process identifier') from err
else:
- return load_plugin('process', process_conf['processor'])
+ pp = load_plugin('process', process_conf['processor'])
+ pp.allow_internal_requests = process_conf.get(
+ 'allow_internal_requests', False)
+
+ return pp
def get_jobs(self,
status: JobStatus = None,
@@ -394,13 +400,13 @@ class BaseManager:
"""
job_id = str(uuid.uuid1())
- processor = self.get_processor(process_id)
- processor.set_job_id(job_id)
+ self.processor = self.get_processor(process_id)
+ self.processor.set_job_id(job_id)
extra_execute_handler_parameters = {
'requested_response': requested_response
}
- job_control_options = processor.metadata.get(
+ job_control_options = self.processor.metadata.get(
'jobControlOptions', [])
if execution_mode == RequestedProcessExecutionMode.respond_async:
@@ -473,7 +479,7 @@ class BaseManager:
# TODO: handler's response could also be allowed to include more HTTP
# headers
mime_type, outputs, status = handler(
- processor,
+ self.processor,
job_id,
data_dict,
requested_outputs,
@@ -483,26 +489,37 @@ class BaseManager:
def _send_in_progress_notification(self, subscriber: Optional[Subscriber]):
if subscriber and subscriber.in_progress_uri:
- response = requests.post(subscriber.in_progress_uri, json={})
- LOGGER.debug(
- f'In progress notification response: {response.status_code}'
- )
+ self.__do_subscriber_request(subscriber.in_progress_uri)
def _send_success_notification(
self, subscriber: Optional[Subscriber], outputs: Any
):
- if subscriber:
- response = requests.post(subscriber.success_uri, json=outputs)
- LOGGER.debug(
- f'Success notification response: {response.status_code}'
- )
+ if subscriber and subscriber.success_uri:
+ self.__do_subscriber_request(subscriber.success_uri, outputs)
def _send_failed_notification(self, subscriber: Optional[Subscriber]):
if subscriber and subscriber.failed_uri:
- response = requests.post(subscriber.failed_uri, json={})
- LOGGER.debug(
- f'Failed notification response: {response.status_code}'
- )
+ self.__do_subscriber_request(subscriber.failed_uri)
+
+ def __do_subscriber_request(self, url: str, data: dict = {}) -> None:
+ """
+ Helper function to execute a subscriber URL via HTTP POST
+
+ :param url: `str` of URL
+ :param data: `dict` of request payload
+
+ :returns: `None`
+ """
+
+ if not is_request_allowed(url, self.processor.allow_internal_requests):
+ msg = 'URL not allowed'
+ LOGGER.error(f'{msg}: {url}')
+ raise ProcessorExecuteError(msg)
+
+ response = requests.post(url, json=data)
+ LOGGER.debug(
+ f'Response: {response.status_code}'
+ )
def __repr__(self):
return f'<BaseManager> {self.name}'
=====================================
pygeoapi/provider/filesystem.py
=====================================
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis at gmail.com>
#
-# Copyright (c) 2023 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -34,6 +34,7 @@ import logging
import os
from pygeoapi.provider.base import (BaseProvider, ProviderConnectionError,
+ ProviderInvalidQueryError,
ProviderNotFoundError)
from pygeoapi.util import file_modified_iso8601, get_path_basename, url_join
@@ -76,9 +77,15 @@ class FileSystemProvider(BaseProvider):
root_link = None
child_links = []
- data_path = os.path.join(self.data, dirpath)
+ if '..' in dirpath:
+ msg = 'Invalid path requested'
+ LOGGER.error(f'{msg}: {dirpath}')
+ raise ProviderInvalidQueryError(msg)
+
data_path = self.data + dirpath
+ LOGGER.debug(f'Data path: {data_path}')
+
if '/' not in dirpath: # root
root_link = baseurl
else:
=====================================
pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml
=====================================
@@ -682,7 +682,11 @@ properties:
For custom built plugins, use the import path (e.g. `mypackage.provider.MyProvider`)
required:
- name
- required:
+ allow_internal_requests:
+ type: boolean
+ description: whether to allow internal HTTP requests
+ default: false
+ requred:
- type
- processor
definitions:
=====================================
pygeoapi/util.py
=====================================
@@ -36,6 +36,7 @@ from datetime import date, datetime, time, timezone
from decimal import Decimal
from enum import Enum
from heapq import heappush
+import ipaddress
import json
import logging
import mimetypes
@@ -43,6 +44,7 @@ import os
import pathlib
from pathlib import Path
import re
+import socket
from typing import Any, IO, Union, List, Optional
from urllib.parse import urlparse
from urllib.request import urlopen
@@ -754,3 +756,30 @@ def remove_url_auth(url: str) -> str:
u = urlparse(url)
auth = f'{u.username}:{u.password}@'
return url.replace(auth, '')
+
+
+def is_request_allowed(url: str, allow_internal: bool = False) -> bool:
+ """
+ Test whether an HTTP request is allowed to be executed
+
+ :param url: `str` of URL
+ :param allow_internal: `bool` of whether internal requests are
+ allowed (default `False`)
+
+ :returns: `bool` of whether HTTP request execution is allowed
+ """
+
+ is_allowed = False
+
+ u = urlparse(url)
+
+ ip = socket.gethostbyname(u.hostname)
+
+ is_private = ipaddress.ip_address(ip).is_private
+
+ if not is_private:
+ is_allowed = True
+ if is_private and allow_internal:
+ is_allowed = True
+
+ return is_allowed
=====================================
requirements.txt
=====================================
@@ -1,18 +1,18 @@
-Babel==2.10.3
-click==8.1.6
-filelock==3.25.0
-Flask==3.1.3
-jinja2==3.1.2
-jsonschema==4.26.0
-pydantic==1.10.14
-pygeofilter==0.3.3
-pygeoif==1.6.0
-pyproj==3.6.1
-python-dateutil==2.8.2
-pytz==2026.1.post1
-PyYAML==6.0.1
-rasterio==1.3.9
-requests==2.31.0
-shapely==2.0.3
-SQLAlchemy==2.0.48
-tinydb==3.15.2
+Babel
+click
+filelock
+Flask
+jinja2
+jsonschema
+pydantic
+pygeofilter
+pygeoif
+pyproj
+python-dateutil
+pytz
+PyYAML
+rasterio
+requests
+shapely
+SQLAlchemy
+tinydb
=====================================
tests/other/test_util.py
=====================================
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis at gmail.com>
#
-# Copyright (c) 2025 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -321,3 +321,21 @@ def test_get_choice_from_headers():
'accept') == 'application/ld+json'
assert util.get_choice_from_headers(
{'accept-language': 'en_US', 'accept': '*/*'}, 'accept') == '*/*'
+
+
+ at pytest.mark.parametrize('url,allow_internal,result', [
+ ['http://127.0.0.1/test', False, False],
+ ['http://127.0.0.1/test', True, True],
+ ['http://192.168.0.12/test', False, False],
+ ['http://192.168.0.12/test', True, True],
+ ['http://169.254.0.11/test', False, False],
+ ['http://169.254.0.11/test', True, True],
+ ['http://0.0.0.0/test', True, True],
+ ['http://0.0.0.0/test', False, False],
+ ['http://localhost:5000/test', False, False],
+ ['http://localhost:5000/test', True, True],
+ ['https://pygeoapi.io', False, True],
+ ['https://pygeoapi.io', True, True]
+])
+def test_is_request_allowed(url, allow_internal, result):
+ assert util.is_request_allowed(url, allow_internal) is result
=====================================
tests/provider/test_filesystem_provider.py
=====================================
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis at gmail.com>
#
-# Copyright (c) 2021 Tom Kralidis
+# Copyright (c) 2026 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -30,6 +30,7 @@
import os
import pytest
+from pygeoapi.provider.base import ProviderInvalidQueryError
from pygeoapi.provider.filesystem import FileSystemProvider
THISDIR = os.path.dirname(os.path.realpath(__file__))
@@ -73,3 +74,6 @@ def test_query(config):
'osm_id': 'int'
}
assert r['assets']['default']['href'] == 'http://example.org/stac/poi_portugal.gpkg' # noqa
+
+ with pytest.raises(ProviderInvalidQueryError):
+ _ = p.get_data_path(baseurl, urlpath, '../../poi_portugal')
View it on GitLab: https://salsa.debian.org/debian-gis-team/pygeoapi/-/compare/459574bf9672ccfd3123a09cd4d3e1e4c6a3b487...34393eae89f654040fdc5b9f1f01bbc7aa5c1fe4
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pygeoapi/-/compare/459574bf9672ccfd3123a09cd4d3e1e4c6a3b487...34393eae89f654040fdc5b9f1f01bbc7aa5c1fe4
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260626/0bf6ff3a/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list