Skip to content

Commit 5c82b44

Browse files
committed
[Processes] Add access policies documentation
1 parent 2bfa048 commit 5c82b44

File tree

6 files changed

+195
-2
lines changed

6 files changed

+195
-2
lines changed

qjazz-processes/doc/configs/server.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ description = "Publish Qgis processing algorithms as OGC api processes"
1212

1313
# Defining job realm allow filtering job's requests by a token that is
1414
# set by the client when requesting task execution (see description below).
15+
#
1516
[job_realm]
1617
#
1718
# Enable job realm header
@@ -231,6 +232,7 @@ enabled = false
231232

232233
# The storage configuration is used for configuring the
233234
# connections to storage backends used by workers.
235+
#
234236
[storage]
235237
#
236238
# Allow insecure downloads

qjazz-processes/doc/configs/worker.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ level = "INFO"
77
# Worker configuration
88
#
99
# Configure celery worker settings
10+
#
1011
[worker]
1112
#
1213
# Celery amqp broker host

qjazz-processes/doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Welcome to qjazz-processes's documentation!
1313
parameters
1414
execution
1515
jobrealm
16+
policies
1617
openapi
1718

1819
Indices and tables

qjazz-processes/doc/policies.rst

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
.. _access_policies:
2+
3+
Access policies
4+
===============
5+
6+
Access policies control:
7+
8+
- Which services a request can access
9+
- Which processes a user can execute
10+
- How services and projects are resolved from requests
11+
- How URLs are formatted for process links
12+
13+
Implementing custom access policies
14+
-----------------------------------
15+
16+
Custom access policies may be implemented from external modules.
17+
18+
You may configure custom policies from the [access_policy] section of the
19+
server configuration:
20+
21+
.. code-block:: toml
22+
23+
[access_policy]
24+
#
25+
# Access policy module
26+
#
27+
# The module implementing the access policy for
28+
# processes execution.
29+
#
30+
policy_class = "mymodule.MyAccessPolicy"
31+
32+
# The configuration of the custom policies in TOML
33+
# format
34+
[accesspolicy.config]
35+
36+
37+
AccessPolicy Protocol
38+
---------------------
39+
40+
The ``policy_class`` must be an import string pointing to a class implementing the ``AccessPolicy`` protocol:
41+
42+
43+
Class Attributes
44+
~~~~~~~~~~~~~~~~
45+
46+
``Config``: ``type[ConfigBase]``
47+
A Pydantic model class for configuration. Defaults to ``NoConfig``.
48+
49+
The configuration will be validated against this model.
50+
51+
``executor``: ``AsyncExecutor``
52+
Set automatically by the framework. Provides access to available services.
53+
54+
Methods
55+
~~~~~~~
56+
57+
``setup(app: web.Application) -> None``
58+
Called when the policy is initialized. Use to set up any resources.
59+
60+
*Default*: No-op.
61+
62+
``service_permission(request: aiohttp.web.Request, service: str) -> bool``
63+
Check if the request has permission to access the specified service.
64+
65+
``execute_permission(request: web.Request, service: str, process_id: str, project: Optional[str] -> bool``
66+
Check if the request has permission to execute the given process.
67+
68+
``get_service(request: aiohttp.web.Request) -> str``
69+
Resolve and return the service identifier for the request.
70+
Called when a request requires service resolution.
71+
72+
``get_project(request: aiohttp.web.Request) -> Optional[str]``
73+
Resolve and return the project (QGIS project path) for the request.
74+
Returns ``None`` if no project is specified.
75+
76+
``prefix: str`` (property)
77+
URL prefix for the policy. Used in path formatting.
78+
79+
*Default*: Empty string.
80+
81+
``format_path(request: aiohttp.web.Request, path: str, service: Optional[str] = None, project: Optional[str] = None, *, query: Optional[str] = None) -> str``
82+
Format a complete URL including service and project parameters.
83+
Used to generate process links in API responses.
84+
85+
Implementing a Custom Policy
86+
-----------------------------
87+
88+
1. Create a Python package with your policy class:
89+
90+
.. code:: python
91+
92+
# my_policy/__init__.py
93+
from my_policy.config import MyPolicyConfig
94+
from my_policy.policy import MyAccessPolicy
95+
96+
2. Define a configuration model:
97+
98+
.. code:: python
99+
100+
# my_policy/config.py
101+
from qjazz_core.config import ConfigBase
102+
from qjazz_core.models import Field
103+
104+
105+
class MyPolicyConfig(ConfigBase):
106+
allowed_services: list[str] = Field(
107+
default=[],
108+
description="List of allowed service names",
109+
)
110+
111+
3. Implement the policy class:
112+
113+
.. code:: python
114+
115+
# my_policy/policy.py
116+
from typing import Optional
117+
from aiohttp import web
118+
from qjazz_processes.server.accesspolicy import AccessPolicy
119+
from qjazz_core.config import ConfigBase
120+
121+
122+
class MyAccessPolicy(AccessPolicy):
123+
Config = MyPolicyConfig
124+
125+
def __init__(self, conf: MyPolicyConfig):
126+
self._allowed = set(conf.allowed_services)
127+
128+
def setup(self, app: web.Application):
129+
# Initialize resources if needed
130+
pass
131+
132+
def service_permission(self, request: web.Request, service: str) -> bool:
133+
return service in self._allowed
134+
135+
def execute_permission(
136+
self,
137+
request: web.Request,
138+
service: str,
139+
process_id: str,
140+
project: Optional[str] = None,
141+
) -> bool:
142+
# Add custom logic (e.g., check user permissions)
143+
return True
144+
145+
def get_service(self, request: web.Request) -> str:
146+
return request.query.get("service", "")
147+
148+
def get_project(self, request: web.Request) -> Optional[str]:
149+
return request.query.get("map")
150+
151+
@property
152+
def prefix(self) -> str:
153+
return "/custom"
154+
155+
def format_path(
156+
self,
157+
request: web.Request,
158+
path: str,
159+
service: Optional[str] = None,
160+
project: Optional[str] = None,
161+
*,
162+
query: Optional[str] = None,
163+
) -> str:
164+
# Build URL with custom logic
165+
return f"{self.prefix}{path}?service={service}&map={project}"
166+
167+
4. Configure the policy in your server settings:
168+
169+
.. code:: python
170+
171+
AccessPolicyConfig(
172+
policy_class="my_policy.policy.MyAccessPolicy",
173+
config={"allowed_services": ["wfs", "wms"]},
174+
)

qjazz-processes/src/qjazz_processes/server/accesspolicy.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
""" Access policy configuration
2+
3+
Access policies control:
4+
5+
- Which services a request can access
6+
- Which processes a user can execute
7+
- How services and projects are resolved from requests
8+
- How URLs are formatted for process links
9+
10+
Custom access policies can be implemented in external python module.
11+
"""
112
from abc import abstractmethod
213
from typing import (
314
Annotated,
@@ -80,7 +91,7 @@ def get_service(self, request: web.Request) -> str:
8091

8192
@abstractmethod
8293
def get_project(self, request: web.Request) -> Optional[str]:
83-
"""Return project for therequest"""
94+
"""Return project for the request"""
8495
...
8596

8697
@property
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
# reimport
2-
from .default import DefaultAccessPolicy # noqa F401
2+
from .default import DefaultAccessPolicy
3+
4+
__all__ = (
5+
"DefaultAccessPolicy",
6+
)

0 commit comments

Comments
 (0)