splitting out contrib plugin documentation into separate pages

pull/281/head
Andrew Mirsky 2025-07-30 09:32:56 -04:00
rodzic c518b9b63f
commit f6ccdbc51c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: A98E67635CDF2C39
15 zmienionych plików z 123 dodań i 75 usunięć

Wyświetl plik

@ -129,7 +129,9 @@ class Broker:
plugin_namespace: plugin namespace to use when loading plugin entry_points. defaults to `amqtt.broker.plugins`.
Raises:
BrokerError, ParserError, PluginError
BrokerError: problem with broker configuration
PluginImportError: if importing a plugin from configuration
PluginInitError: if initialization plugin fails
"""

Wyświetl plik

@ -86,7 +86,8 @@ class MQTTClient:
config: dictionary of configuration options (see [client configuration](client_config.md)).
Raises:
PluginError
PluginImportError: if importing a plugin from configuration
PluginInitError: if initialization plugin fails
"""
@ -145,7 +146,7 @@ class MQTTClient:
[CONNACK](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033)'s return code
Raises:
ClientError, ConnectError
ConnectError: could not connect to broker
"""
additional_headers = additional_headers if additional_headers is not None else {}
@ -380,6 +381,7 @@ class MQTTClient:
Raises:
asyncio.TimeoutError: if timeout occurs before a message is delivered
ClientError: if client is not connected
"""
if self._handler is None:

Wyświetl plik

@ -1,5 +1,5 @@
import logging
from dataclasses import dataclass, field, fields, replace
import logging
try:
from enum import Enum, StrEnum
@ -9,15 +9,14 @@ except ImportError:
class StrEnum(str, Enum): #type: ignore[no-redef]
pass
from pathlib import Path
from collections.abc import Iterator
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal
from dacite import Config as DaciteConfig, from_dict as dict_to_dataclass
from amqtt.mqtt.constants import QOS_0, QOS_2
if TYPE_CHECKING:
import asyncio
@ -155,9 +154,9 @@ class BrokerConfig(Dictable):
listeners: dict[Literal["default"] | str, ListenerConfig] = field(default_factory=default_listeners) # noqa: PYI051
"""Network of listeners used by the services. a 'default' named listener is required; if another listener
does not set a value, the 'default' settings are applied. See
[ListenerConfig](./#amqtt.contexts.ListenerConfig) for more information."""
[ListenerConfig](#amqtt.contexts.ListenerConfig) for more information."""
sys_interval: int | None = None
"""*Deprecated field to configure the `BrokerSysPlugin`. See [`BrokerSysPlugin`](../packaged_plugins.md/#sys-topics)
"""*Deprecated field to configure the `BrokerSysPlugin`. See [`BrokerSysPlugin`](#sys-topics)
for recommended configuration.*"""
timeout_disconnect_delay: int | None = 0
"""Client disconnect timeout without a keep-alive."""
@ -165,8 +164,8 @@ class BrokerConfig(Dictable):
"""Seconds for an inactive session to be retained."""
auth: dict[str, Any] | None = None
"""*Deprecated field used to config EntryPoint-loaded plugins. See
[`AnonymousAuthPlugin`](./#anonymous-auth-plugin) and
[`FileAuthPlugin`](/packaged_plugins/#password-file-auth-plugin) for recommended configuration.*"""
[`AnonymousAuthPlugin`](#anonymous-auth-plugin) and
[`FileAuthPlugin`](#password-file-auth-plugin) for recommended configuration.*"""
topic_check: dict[str, Any] | None = None
"""*Deprecated field used to config EntryPoint-loaded plugins. See
[`TopicTabooPlugin`](#taboo-topic-plugin) and
@ -328,7 +327,7 @@ class ClientConfig(Dictable):
"""Specify the topics and what flags should be set for messages published to them."""
broker: ConnectionConfig | None = field(default_factory=ConnectionConfig)
"""Configuration for connecting to the broker. See
[ConnectionConfig](./#amqtt.contexts.ConnectionConfig) for more information."""
[ConnectionConfig](#amqtt.contexts.ConnectionConfig) for more information."""
plugins: dict[str, Any] | list[dict[str, Any]] | None = field(default_factory=default_client_plugins)
"""The dictionary has a key of the dotted-module path of a class derived from `BasePlugin`; the value is
a dictionary of configuration options for that plugin. See [Plugins](http://localhost:8000/custom_plugins/)
@ -337,7 +336,7 @@ class ClientConfig(Dictable):
"""If establishing a secure connection, should the hostname of the certificate be verified."""
will: WillConfig | None = None
"""Message, topic and flags that should be sent to if the client disconnects. See
[WillConfig](./#amqtt.contexts.WillConfig)"""
[WillConfig](#amqtt.contexts.WillConfig)"""
def __post__init__(self) -> None:
"""Check config for errors and transform fields for easier use."""

Wyświetl plik

@ -56,14 +56,15 @@ class UserAuthDBPlugin(BaseAuthPlugin, BaseTopicPlugin):
connection: str
"""SQLAlchemy connection string for the asyncio version of the database connector:
- mysql+aiomysql://user:password@host:port/dbname
- postgresql+asyncpg://user:password@host:port/dbname
- sqlite+aiosqlite:///dbfilename.db
- `mysql+aiomysql://user:password@host:port/dbname`
- `postgresql+asyncpg://user:password@host:port/dbname`
- `sqlite+aiosqlite:///dbfilename.db`
"""
sync_schema: bool = False
"""Use SQLAlchemy to create / update the database schema."""
hash_schemes: list[str] = field(default_factory=default_hash_scheme)
"""list of hash schemes to use for passwords"""
class TopicAuthDBPlugin(BaseTopicPlugin):
@ -100,9 +101,10 @@ class TopicAuthDBPlugin(BaseTopicPlugin):
connection: str
"""SQLAlchemy connection string for the asyncio version of the database connector:
- mysql+aiomysql://user:password@host:port/dbname
- postgresql+asyncpg://user:password@host:port/dbname
- sqlite+aiosqlite:///dbfilename.db
- `mysql+aiomysql://user:password@host:port/dbname`
- `postgresql+asyncpg://user:password@host:port/dbname`
- `sqlite+aiosqlite:///dbfilename.db`
"""
sync_schema: bool = False
"""Use SQLAlchemy to create / update the database schema."""

Wyświetl plik

@ -48,7 +48,7 @@ HTTP_4xx_MIN = 400
HTTP_4xx_MAX = 499
class HttpAuthTopicPlugin(BaseAuthPlugin, BaseTopicPlugin):
class HttpAuthPlugin(BaseAuthPlugin, BaseTopicPlugin):
def __init__(self, context: BrokerContext) -> None:
super().__init__(context)
@ -167,7 +167,9 @@ class HttpAuthTopicPlugin(BaseAuthPlugin, BaseTopicPlugin):
"""
host: str
"""hostname of the server for the auth & acl check"""
port: int
"""port of the server for the auth & acl check"""
user_uri: str
topic_uri: str
request_method: RequestMethod = RequestMethod.GET

Wyświetl plik

@ -0,0 +1,39 @@
# Relational Database for Authentication and Authorization
- `amqtt.contrib.auth_db.AuthUserDBPlugin` (authentication) verify a client's ability to connect to broker
- `amqtt.contrib.auth_db.AuthTopicDBPlugin` (authorization) determine a client's access to topics
Relational database access is supported using SQLAlchemy so MySQL, MariaDB, Postgres and SQLite support is available.
For ease of use, the [`user_mgr` command-line utility](auth_db.md/#user_mgr) to add, remove, update and
list clients. And the [`topic_mgr` command-line utility](auth_db.md/#topic_mgr) to add client access to
subscribe, publish and receive messages on topics.
## Authentication Configuration
::: amqtt.contrib.auth_db.UserAuthDBPlugin.Config
options:
heading_level: 4
extra:
class_style: "simple"
## Authorization Configuration
::: amqtt.contrib.auth_db.TopicAuthDBPlugin.Config
options:
heading_level: 4
extra:
class_style: "simple"
## CLI
::: mkdocs-typer2
:module: amqtt.contrib.auth_db.user_mgr_cli
:name: user_mgr
::: mkdocs-typer2
:module: amqtt.contrib.auth_db.topic_mgr_cli
:name: topic_mgr

Wyświetl plik

@ -0,0 +1,19 @@
# Contributed Plugins
These are fully supported plugins but require additional dependencies to be installed:
`$ pip install '.[contrib]'`
- Relational Database Auth<br/>
_includes manager script to add, remove and create db entries_
- [DB Client Authentication](auth_db.md)<br/>
Authenticate a client's connection to broker based on entries in a relational db (mysql, postgres, maria, sqlite).<br/>
`amqtt.contrib.auth_db.AuthUserDBPlugin`
- [DB Client Authorization](auth_db.md)<br/>
Determine a client's access to topics.<br/>
`amqtt.contrib.auth_db.AuthTopicDBPlugin`
- [HTTP Auth](http.md)<br/>
Determine client authentication and authorization based on response from a separate HTTP server.<br/>
`amqtt.contrib.http.HttpAuthTopicPlugin`

Wyświetl plik

@ -77,7 +77,9 @@ and then run via `amqtt -c myBroker.yaml`.
variables to configure its behavior.
::: amqtt.plugins.base.BasePlugin
options:
show_source: false
heading_level: 3
## Events
@ -85,16 +87,13 @@ and then run via `amqtt -c myBroker.yaml`.
All plugins are notified of events if the `BasePlugin` subclass implements one or more of these methods:
### Client and Broker
### Client
- `async def on_mqtt_packet_sent(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
- `async def on_mqtt_packet_received(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
### Client Only
none
### Broker Only
### Broker
- `async def on_broker_pre_start(self) -> None`
- `async def on_broker_post_start(self) -> None`
@ -111,6 +110,9 @@ none
- `async def on_broker_client_unsubscribed(self, *, client_id: str, topic: str) -> None`
- `async def on_broker_message_received(self, *, client_id: str, message: ApplicationMessage) -> None`
- `async def on_broker_message_broadcast(self, *, client_id: str, message: ApplicationMessage) -> None`
- `async def on_mqtt_packet_sent(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
- `async def on_mqtt_packet_received(self, *, packet: MQTTPacket[MQTTVariableHeader, MQTTPayload[MQTTVariableHeader], MQTTFixedHeader], session: Session | None = None) -> None`
## Authentication Plugins
@ -128,6 +130,9 @@ If there are multiple authentication plugins:
- `None` gets ignored from the determination
::: amqtt.plugins.base.BaseAuthPlugin
options:
show_source: false
heading_level: 3
## Topic Filter Plugins
@ -144,7 +149,9 @@ If there are multiple topic plugins:
- `None` will be ignored
::: amqtt.plugins.base.BaseTopicPlugin
options:
show_source: false
heading_level: 3
!!! note
A custom plugin class can subclass from both `BaseAuthPlugin` and `BaseTopicPlugin` as long it defines

Wyświetl plik

@ -1,40 +1,3 @@
# Contributed Plugins
Plugins that are not part of the core functionality of the aMQTT broker or client and require additional dependencies:
`$ pip install '.[contrib]'`
# Relational Database for Authentication and Authorization
- `amqtt.contrib.auth_db.AuthUserDBPlugin` (authentication) verify a client's ability to connect to broker
- `amqtt.contrib.auth_db.AuthTopicDBPlugine` (authorization) determine a client's access to topics
Relational database access is supported using SQLAlchemy so MySQL, MariaDB, Postgres and SQLite support is available.
For ease of use, the [`user_mgr` command-line utility](contrib_plugins.md/#user_mgr) to add, remove, update and
list clients. And the [`topic_mgr` command-line utility](contrib_plugins.md/#user_topic) to add client access to
subscribe, publish and receive messages on topics.
## Authentication Configuration
::: amqtt.contrib.auth_db.UserAuthDBPlugin.Config
## Authorization Configuration
::: amqtt.contrib.auth_db.TopicAuthDBPlugin.Config
## Command line for authentication
::: mkdocs-typer2
:module: amqtt.contrib.auth_db.user_mgr_cli
:name: user_mgr
## Command line for authorization
::: mkdocs-typer2
:module: amqtt.contrib.auth_db.topic_mgr_cli
:name: topic_mgr
# Authentication & Topic Access via external HTTP server
`amqtt.contrib.http.HttpAuthTopicPlugin`
@ -43,6 +6,11 @@ If clients accessing the broker are managed by another application, implement AP
that allows the broker to check if a client is authenticated and what topics that client
is authorized to access.
::: amqtt.contrib.http.HttpAuthPlugin.Config
options:
show_source: false
heading_level: 4
**Configuration**
- `host` *(str) hostname of the server for the auth & acl check

Wyświetl plik

@ -23,7 +23,7 @@ and configured for the broker:
--8<-- "pyproject.toml:included"
```
But the same 4 plugins were activated in the previous default config:
But the previous default config only caused 4 plugins to be active:
```yaml
--8<-- "samples/legacy.yaml"
@ -248,7 +248,7 @@ plugins:
`amqtt.plugins.logging_amqtt.PacketLoggerPlugin`
This plugin issues debug-level messages for [mqtt events](custom_plugins.md#client-and-broker): `on_mqtt_packet_sent`
This plugin issues debug-level messages for [mqtt events](custom_plugins.md#events): `on_mqtt_packet_sent`
and `on_mqtt_packet_received`.
```yaml

Wyświetl plik

@ -32,4 +32,5 @@ used by the included broker script: [broker configuration](broker_config.md)
::: amqtt.broker.Broker
[^1]: See [PyYAML](http://pyyaml.org/wiki/PyYAMLDocumentation) for loading YAML files as Python dict.

Wyświetl plik

@ -10,11 +10,13 @@ from _griffe.agents.visitor import Visitor
from _griffe.models import Attribute
from amqtt.contexts import default_listeners, default_broker_plugins, default_client_plugins
from amqtt.contrib.auth_db.plugin import default_hash_scheme
default_factory_map = {
'default_listeners': default_listeners(),
'default_broker_plugins': default_broker_plugins(),
'default_client_plugins': default_client_plugins()
'default_client_plugins': default_client_plugins(),
'default_hash_scheme': default_hash_scheme()
}
def get_qualified_name(node: ast.AST) -> str | None:

1
docs/templates/README vendored 100644
Wyświetl plik

@ -0,0 +1 @@
template overrides for mkdocs-materials

Wyświetl plik

@ -38,9 +38,12 @@ nav:
- Client: references/client.md
- Common: references/common.md
- Plugins:
- Packaged: packaged_plugins.md
- Custom: custom_plugins.md
- Contributed: contrib_plugins.md
- Packaged: plugins/packaged_plugins.md
- Custom: plugins/custom_plugins.md
- Contributed:
- plugins/contrib.md
- Database Auth: plugins/auth_db.md
- HTTP Auth: plugins/http.md
- Configuration:
- Broker: references/broker_config.md
- Client: references/client_config.md
@ -58,6 +61,7 @@ theme:
name: material
logo: assets/amqtt_bw.svg
features:
- toc.integrate
- announce.dismiss
- content.action.edit
- content.action.view
@ -118,6 +122,7 @@ markdown_extensions:
- pymdownx.tilde
- toc:
permalink: "¤"
toc_depth: 3
plugins:
- search
@ -135,7 +140,6 @@ plugins:
options:
# extra:
# template_log_display: true
annotation_path: "full"
docstring_options:
ignore_init_summary: true
docstring_section_style: list

Wyświetl plik

@ -8,7 +8,7 @@ from aiohttp.web import Response
from amqtt.broker import BrokerContext, Broker
from amqtt.contexts import Action
from amqtt.contrib.http import HttpAuthTopicPlugin, ParamsMode, ResponseMode, RequestMethod
from amqtt.contrib.http import HttpAuthPlugin, ParamsMode, ResponseMode, RequestMethod
from amqtt.session import Session
logger = logging.getLogger(__name__)
@ -135,7 +135,7 @@ async def test_request_acl_response(empty_broker, http_server, kind, url,
username, matcher, is_allowed):
context = BrokerContext(broker=empty_broker)
context.config = HttpAuthTopicPlugin.Config(
context.config = HttpAuthPlugin.Config(
host="127.0.0.1",
port=8080,
user_uri=url,
@ -144,7 +144,7 @@ async def test_request_acl_response(empty_broker, http_server, kind, url,
params_mode=params_mode,
response_mode=response_mode,
)
http_acl = HttpAuthTopicPlugin(context)
http_acl = HttpAuthPlugin(context)
logger.warning(f'kind is {kind}')
if kind == TestKind.ACL:
s = Session()