diff --git a/docs/custom_plugins.md b/docs/custom_plugins.md index 43902a3..da2d56b 100644 --- a/docs/custom_plugins.md +++ b/docs/custom_plugins.md @@ -1,37 +1,73 @@ +from dataclasses import dataclass + # Custom Plugins -With the aMQTT Broker plugins framework, one can add additional functionality to the broker without -having to subclass or rewrite any of the core broker logic. To define a custom list of plugins to be loaded, -add this section to your `pyproject.toml`" +With the aMQTT plugins framework, one can add additional functionality to the client or broker without +having to rewrite any of the core logic. -```toml -[project.entry-points."mypackage.mymodule.plugins"] -plugin_alias = "module.submodule.file:ClassName" -``` - -and specify the namespace when instantiating the broker: +To create a custom plugin, subclass from `BasePlugin` (client or broker) or `BaseAuthPlugin` (broker only) +or `BaseTopicPlugin` (broker only). Each custom plugin may define settings specific to itself by creating +a nested (or inner) `dataclass` named `Config` which declares each option and a default value (if applicable). A +plugin's configuration dataclass will be type-checked and made available from within the `self.context` instance variable. ```python -from amqtt.broker import Broker +from dataclasses import dataclass, field +from amqtt.plugins.base import BasePlugin +from amqtt.contexts import BaseContext -broker = Broker(plugin_namespace='mypackage.mymodule.plugins') + +class OneClassName(BasePlugin[BaseContext]): + """This is a plugin with no functionality""" + + +class TwoClassName(BasePlugin[BaseContext]): + """This is a plugin with configuration options.""" + def __init__(self, context: BaseContext): + super().__init__(context) + my_option_one: str = self.context.config.option1 + + @dataclass + class Config: + option1: int + option3: str = field(default="my_default_value") ``` -Each plugin has access to the full configuration file through the provided `BaseContext` and can define -its own variables to configure its behavior. +This plugin class then should be added to the configuration file of the broker or client (or to the `config` +dictionary passed to the `Broker` or `MQTTClient`). + +```yaml +... +... +plugins: + - module.submodule.file.OneClassName: + - module.submodule.file.TwoClassName: + option1: 123 +``` + +??? warning "Deprecated: activating plugins using `EntryPoints`" + With the aMQTT plugins framework, one can add additional functionality to the client or broker without + having to rewrite any of the core logic. To define a custom list of plugins to be loaded, add this section + to your `pyproject.toml`" + + ```toml + [project.entry-points."mypackage.mymodule.plugins"] + plugin_alias = "module.submodule.file:ClassName" + ``` + + Each plugin has access to the full configuration file through the provided `BaseContext` and can define its own + variables to configure its behavior. ::: amqtt.plugins.base.BasePlugin ## Events -Plugins that are defined in the`project.entry-points` are notified of events if the subclass -implements one or more of these methods: +All plugins are notified of events if the `BasePlugin` subclass implements one or more of these methods: ### Client and Broker -- `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` +- `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 @@ -55,32 +91,22 @@ none ## Authentication Plugins -Of the plugins listed in `project.entry-points`, one or more can be used to validate client sessions -by specifying their alias in `auth` > `plugins` section of the config: - -```yaml -auth: - plugins: - - plugin_alias_name -``` - -These plugins should subclass from `BaseAuthPlugin` and implement the `authenticate` method. +In addition to receiving any of the event callbacks, a plugin which subclasses from `BaseAuthPlugin` +is used by the aMQTT `Broker` to determine if a connection from a client is allowed by +implementing the `authenticate` method and returning `True` if the session is allowed or `False` otherwise. ::: amqtt.plugins.base.BaseAuthPlugin ## Topic Filter Plugins -Of the plugins listed in `project.entry-points`, one or more can be used to determine topic access -by specifying their alias in `topic-check` > `plugins` section of the config: - -```yaml -topic-check: - enable: True - plugins: - - plugin_alias_name -``` - -These plugins should subclass from `BaseTopicPlugin` and implement the `topic_filtering` method. - +In addition to receiving any of the event callbacks, a plugin which is subclassed from `BaseTopicPlugin` +is used by the aMQTT `Broker` to determine if a connected client can send (PUBLISH) or receive (SUBSCRIBE) +messages to a particular topic by implementing the `topic_filtering` method and returning `True` if allowed or +`False` otherwise. ::: amqtt.plugins.base.BaseTopicPlugin + + +!!! note + A custom plugin class can subclass from both `BaseAuthPlugin` and `BaseTopicPlugin` as long it defines + both the `authenticate` and `topic_filtering` method. diff --git a/docs/references/broker_config.md b/docs/references/broker_config.md index 30ec0d1..7afbc9d 100644 --- a/docs/references/broker_config.md +++ b/docs/references/broker_config.md @@ -3,7 +3,7 @@ This configuration structure is valid as a python dictionary passed to the `amqtt.broker.Broker` class's `__init__` method or as a yaml formatted file passed to the `amqtt` script. -### `listeners` *(list[mapping])* +### `listeners` *(list[dict[str, Any]])* Defines the network listeners used by the service. Items defined in the `default` listener will be applied to all other listeners, unless they are overridden by the configuration for the specific @@ -20,77 +20,77 @@ listener. - `certfile` *(string)*: Path to a single file in PEM format containing the certificate as well as any number of CA certificates needed to establish the certificate's authenticity. - `keyfile` *(string): A file containing the private key. Otherwise the private key will be taken from `certfile` as well. -### `sys_interval` *(int)* - -System status report interval in seconds (`broker_sys` plugin) - ### `timeout-disconnect-delay` *(int)* Client disconnect timeout without a keep-alive -### `auth` *(mapping)* +### `plugins` *(mapping)* -Configuration for authentication behaviour: - -- `plugins` *(list[string])*: defines the list of plugins which are activated as authentication plugins. - - !!! note "Entry points" - Plugins used here must first be defined in the `amqtt.broker.plugins` [entry point](https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata). +A list of strings representing the modules and class name of `BasePlugin`, `BaseAuthPlugin` and `BaseTopicPlugins`. Each +entry may have one or more configuration settings. For more information, see the [configuration of the included plugins](../packaged_plugins.md) - !!! danger "Legacy behavior" - if `plugins` is omitted from the `auth` section, all plugins listed in the `amqtt.broker.plugins` entrypoint will be enabled - for authentication, *including allowing anonymous login.* - - `plugins: []` will deny connections from all clients. - -- `allow-anonymous` *(bool)*: `True` will allow anonymous connections. - - *Used by the internal `amqtt.plugins.authentication.AnonymousAuthPlugin` plugin* +??? warning "Deprecated: `sys_interval` " + **`sys_interval`** *(int)* - !!! danger "Username only connections" - `False` does not disable the `auth_anonymous` plugin; connections will still be allowed as long as a username is provided. - - If security is required, do not include `auth_anonymous` in the `plugins` list. + System status report interval in seconds, used by the `amqtt.plugins.sys.broker.BrokerSysPlugin`. -- `password-file` *(string)*: Path to file which includes `username:password` pair, one per line. The password should be encoded using sha-512 with `mkpasswd -m sha-512` or: - ```python - import sys - from getpass import getpass - from passlib.hash import sha512_crypt - - passwd = input() if not sys.stdin.isatty() else getpass() - print(sha512_crypt.hash(passwd)) - ``` - - *Used by the internal `amqtt.plugins.authentication.FileAuthPlugin` plugin.* -### `topic-check` *(mapping)* +??? warning "Deprecated: `auth` configuration settings" -Configuration for access control policies for publishing and subscribing to topics: + **`auth`** + + Configuration for authentication behaviour: + + - `plugins` *(list[string])*: defines the list of plugins which are activated as authentication plugins. + + !!! note + Plugins used here must first be defined in the `amqtt.broker.plugins` [entry point](https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata). + -- `enabled` *(bool)*: Enable access control policies (`true`). `false` will allow clients to publish and subscribe to any topic. -- `plugins` *(list[string])*: defines the list of plugins which are activated as access control plugins. Note the plugins must be defined in the `amqtt.broker.plugins` [entry point](https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins). + !!! warning + If `plugins` is omitted from the `auth` section, all plugins listed in the `amqtt.broker.plugins` entrypoint will be enabled + for authentication, including _allowing anonymous login._ + + `plugins: []` will deny connections from all clients. + + - `allow-anonymous` *(bool)*: `True` will allow anonymous connections, used by `amqtt.plugins.authentication.AnonymousAuthPlugin`. + + !!! danger + `False` does not disable the `auth_anonymous` plugin; connections will still be allowed as long as a username is provided. If security is required, do not include `auth_anonymous` in the `plugins` list. + -- `acl` *(list)*: plugin to determine subscription access; if `publish-acl` is not specified, determine both publish and subscription access. - The list should be a key-value pair, where: -`:[, , ...]` *(string, list[string])*: username of the client followed by a list of allowed topics (wildcards are supported: `#`, `+`). + - `password-file` *(string)*. Path to sha-512 encoded password file, used by `amqtt.plugins.authentication.FileAuthPlugin`. - *used by the `amqtt.plugins.topic_acl.TopicAclPlugin`* - -- `publish-acl` *(list)*: plugin to determine publish access. This parameter defines the list of access control rules; each item is a key-value pair, where: -`:[, , ...]` *(string, list[string])*: username of the client followed by a list of allowed topics (wildcards are supported: `#`, `+`). - - !!! info "Reserved usernames" - - - The username `admin` is allowed access to all topic. - - The username `anonymous` will control allowed topics if using the `auth_anonymous` plugin. +??? warning "Deprecated: `topic-check` configuration settings" - *used by the `amqtt.plugins.topic_acl.TopicAclPlugin`* + **`topic-check`** + + Configuration for access control policies for publishing and subscribing to topics: + + - `enabled` *(bool)*: Enable access control policies (`true`). `false` will allow clients to publish and subscribe to any topic. + - `plugins` *(list[string])*: defines the list of plugins which are activated as access control plugins. Note the plugins must be defined in the `amqtt.broker.plugins` [entry point](https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins). + + - `acl` *(list)*: plugin to determine subscription access; if `publish-acl` is not specified, determine both publish and subscription access. + The list should be a key-value pair, where: + `:[, , ...]` *(string, list[string])*: username of the client followed by a list of allowed topics (wildcards are supported: `#`, `+`). + + *used by the `amqtt.plugins.topic_acl.TopicAclPlugin`* + + - `publish-acl` *(list)*: plugin to determine publish access. This parameter defines the list of access control rules; each item is a key-value pair, where: + `:[, , ...]` *(string, list[string])*: username of the client followed by a list of allowed topics (wildcards are supported: `#`, `+`). + + _Reserved usernames (used by the `amqtt.plugins.topic_acl.TopicAclPlugin`)_ + + - The username `admin` is allowed access to all topic. + - The username `anonymous` will control allowed topics if using the `auth_anonymous` plugin. + + + @@ -130,14 +130,13 @@ listeners: certfile: /some/certfile keyfile: /some/key timeout-disconnect-delay: 2 -auth: - plugins: ['auth_anonymous', 'auth_file'] - allow-anonymous: true - password-file: /some/password-file -topic-check: - enabled: true - plugins: ['topic_acl'] - acl: +plugins: + - amqtt.plugins.authentication.AnonymousAuthPlugin: + allow-anonymous: true + - amqtt.plugin.authentication.FileAuthPlugin: + password-file: /some/password-file + - amqtt.plugins.topic_checking.TopicAccessControlListPlugin: + acl: username1: ['repositories/+/master', 'calendar/#', 'data/memes'] username2: [ 'calendar/2025/#', 'data/memes'] anonymous: ['calendar/2025/#'] diff --git a/docs/references/client_config.md b/docs/references/client_config.md index 67f875f..9581364 100644 --- a/docs/references/client_config.md +++ b/docs/references/client_config.md @@ -68,8 +68,8 @@ TLS certificates used to verify the broker's authenticity. - `cafile` *(string)*: Path to a file of concatenated CA certificates in PEM format. See [Certificates](https://docs.python.org/3/library/ssl.html#ssl-certificates) for more info. - `capath` *(string)*: Path to a directory containing several CA certificates in PEM format, following an [OpenSSL specific layout](https://docs.openssl.org/master/man3/SSL_CTX_load_verify_locations/). - `cadata` *(string)*: Either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates. -- -- + + ### `certfile` *(string)* Path to a single file in PEM format containing the certificate as well as any number of CA certificates needed to establish the server certificate's authenticity. @@ -78,6 +78,11 @@ Path to a single file in PEM format containing the certificate as well as any nu Bypass ssl host certificate verification, allowing self-signed certificates +### `plugins` *(mapping)* + +A list of strings representing the modules and class name of any `BasePlugin`s. Each entry may have one or more +configuration settings. For more information, see the [configuration of the included plugins](../packaged_plugins.md) + ## Default Configuration @@ -110,4 +115,7 @@ will: broker: uri: mqtt://localhost:1883 cafile: /path/to/ca/file +plugins: + - amqtt.plugins.logging_amqtt.PacketLoggerPlugin: + ```