Skip to content

Commit 60af91b

Browse files
committed
[#77] Add integration tests verifying authorization requirements
Added test cases for making sure that the MQTT 5 transport uses given credentials and ACLs to enforce authorization. Also added missing documentation and split out design decisions into separate document. Fixes #77
1 parent 47bbc6c commit 60af91b

7 files changed

Lines changed: 385 additions & 123 deletions

File tree

DESIGN.adoc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
= Implementation Design
2+
:toc: preamble
3+
:sectnums:
4+
5+
----
6+
SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation
7+
8+
See the NOTICE file(s) distributed with this work for additional
9+
information regarding copyright ownership.
10+
11+
This program and the accompanying materials are made available under
12+
the terms of the Apache License Version 2.0 which is available at
13+
https://www.apache.org/licenses/LICENSE-2.0
14+
15+
SPDX-FileType: DOCUMENTATION
16+
SPDX-License-Identifier: Apache-2.0
17+
----
18+
19+
== Overview
20+
21+
This document contains information regarding some design decisions made for the implementation of the transport.
22+
23+
[.specitem,oft-sid="dsn~utransport-send-ignore-priority~1",oft-covers="req~utransport-send-qos-mapping~1"]
24+
== Message Priority Mapping
25+
26+
The MQTT 5 standard does not define any mechanism for message prioritization. All messages are being processed with the same priority.
27+
Consequently, the MQTT 5 transport provided by this crate simply ignores the service class set on a uProtocol message being sent.
28+
29+
[.specitem,oft-sid="dsn~utransport-supported-message-delivery~1",oft-covers="req~utransport-delivery-methods~1",oft-needs="impl,utest"]
30+
== Message Delivery
31+
32+
All messages are being received by means of subscribing to relevant topics on the MQTT broker and delivering the messages to listeners that have been registered via `up_rust::UTransport::register_listener`.
33+
The transport spawns a dedicated [tokio task](https://tokio.rs/tokio/tutorial/spawning#tasks) that listens for incoming messages and dispatches them to the registered listeners on the same thread that the message handling task runs on.
34+
35+
Rationale:
36+
The MQTT 5 standard does not provide means to poll the broker for messages but only supports the push model by means of clients subscribing to topic filters.
37+
38+
The transport requires a tokio runtime to execute but does not make any implicit assumption regarding the availability and size of thread pools. This provides for flexibility regarding the environment that the transport can be deployed to but also requires application developers to take care when implementing message listeners, making sure to not block the transport's incoming message handler when processing a dispatched message.
39+
40+
[.specitem,oft-sid="dsn~mqtt5-transport-authorization~1",oft-covers="req~utransport-registerlistener-prevent-unauthorized-access~1,req~utransport-send-prevent-address-spoofing~1",oft-needs="impl,utest"]
41+
## Authorization
42+
43+
This crate provides an implementation of the uProtocol Transport Layer API. This API can be used by client code (uEntities) for sending and/or receiving messages to/from other uEntities. In general, this crate **does not** impose any restrictions on a message's source and sink addresses when handling messages. In particular, the crate itself **does not** verify a uEntity's _identity_ nor checks a uEntity's _authority_ to send and/or receive messages using particular addresses (UUris).
44+
45+
However, the transport implementation provided by this crate supports the configuration of client credentials (username/password and/or X.509 certificate) for authenticating to an MQTT 5 broker during connection establishment.
46+
47+
When a uEntity using this transport tries to connect to a broker using invalid credentials, the broker will return an MQTT _CONNACK_ packet including a corresponding _Reason Code_ back to the client (see section 3.2.2.2 of the MQTT 5 specification for details). This transport's function for connecting to the broker will fail with a `UStatus` error having a `UCode` as defined in <<Mapping of MQTT 5 Reason Codes to UCodes>>.
48+
49+
Most MQTT brokers allow the configuration of _Access Control Lists_ (ACL), which can be used to restrict a client identity's access to certain MQTT topic patterns only. Given a set of ACLs for a uEntity using this transport implementation:
50+
51+
1. When the uEntity tries to send a message using a source and/or sink address which map to an MQTT topic that the client is not authorized to publish to, the broker will return an MQTT _PUBACK_ packet including a corresponding failure _Reason Code_ to the client (see section 3.4.2.1 of the MQTT 5 specification for details).
52+
2. When a uEntity tries to subscribe to messages using a source and/or sink address pattern which results in an MQTT topic that the client is not authorized to subscribe to, the broker will return a corresponding _Reason Code_ in its MQTT _SUBACK_ packet sent back to the client (see section 3.9.3 of the MQTT 5 specification for details).
53+
54+
The transport implementation will fail the invoked function with a `UStatus` error having a `UCode` as defined in <<Mapping of MQTT 5 Reason Codes to UCodes>>.
55+
56+
[#reason-code-mapping]
57+
[.specitem,oft-sid="dsn~mapping-of-reason-codes~1",oft-needs="impl,utest"]
58+
## Mapping of MQTT 5 Reason Codes to UCodes
59+
60+
This transport maps Reason Codes from MQTT 5 packets to uProtocol `UCode`s as follows
61+
62+
[width="50%",cols="1,2"]
63+
|===
64+
| Reason Code | UCode
65+
66+
|`0x86`|`UNAUTHENTICATED`
67+
|`0x87`|`PERMISSION_DENIED`
68+
|`0x88`|`UNAVAILABLE`
69+
|`0x89`|`UNAVAILABLE`
70+
|`0x8C`|`UNAUTHENTICATED`
71+
|`0x96`|`RESOURCE_EXHAUSTED`
72+
|`0x97`|`RESOURCE_EXHAUSTED`
73+
|`0x9F`|`RESOURCE_EXHAUSTED`
74+
|`0xA0`|`RESOURCE_EXHAUSTED`
75+
|===
76+
77+
All other (error) reason codes > 0x80 are mapped to `INTERNAL`.

README.md

Lines changed: 34 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ This library provides a Rust based implementation of the [MQTT 5 uProtocol Trans
44

55
## Getting Started
66

7+
Add the following to the `[dependencies]` section of your `Cargo.toml` file:
8+
9+
```toml
10+
[dependencies]
11+
up-rust = { version = "0.9" }
12+
up-transport-mqtt5 = { version = "0.4" }
13+
```
14+
15+
Please refer to [the crate's Rust Docs](https://docs.rs/up-transport-mqtt5/) and the [examples](./examples/) folder to see how to configure and use the transport.
16+
17+
## Building from Source
18+
719
### Clone the Repository
820

921
```sh
@@ -34,116 +46,36 @@ cargo test
3446

3547
### Running the Examples
3648

37-
The example shows how the transport can be used to publish uProtocol messages from one uEntity and consume these messages on another uEntity.
49+
The examples show how the transport can be used to publish uProtocol messages from one uEntity and consume these messages on another uEntity.
3850

3951
1. Start the Eclipse Mosquitto MQTT broker using Docker Compose:
4052

41-
```bash
42-
docker compose -f tests/mosquitto/docker-compose.yaml up --detach
43-
```
44-
45-
2. Run the Subscriber
46-
47-
The Subscriber supports configuration via command line options and/or environment variables:
48-
49-
```bash
50-
cargo run --example subscriber_example -- --help
51-
```
52-
53-
Run the Subscriber with options appropriate for your MQTT broker. When using the Mosquitto broker started via Docker Compose, then the defaults should work:
54-
55-
```bash
56-
cargo run --example subscriber_example
57-
```
58-
59-
3. Run the Publisher
60-
61-
The Publisher supports configuration via command line options and/or environment variables:
62-
63-
```bash
64-
cargo run --example publisher_example -- --help
65-
```
66-
67-
Run the Publisher with options appropriate for your MQTT broker. When using the Mosquitto broker started via Docker Compose, then the defaults should work:
68-
69-
```bash
70-
cargo run --example publisher_example
71-
```
72-
73-
## Using the Library
74-
75-
Most developers will want to create an instance of the *Mqtt5Transport* struct and use it with the Communication Level API and its default implementation
76-
which are provided by the *up-rust* library.
77-
78-
The libraries need to be added to the `[dependencies]` section of the `Cargo.toml` file:
79-
80-
```toml
81-
[dependencies]
82-
up-rust = { version = "0.9" }
83-
up-transport-mqtt5 = { version = "0.4" }
84-
```
85-
86-
Please refer to the [publisher_example](./examples/publisher_example.rs) and [subscriber_example](./examples/subscriber_example.rs) to see how to initialize and use the transport.
87-
88-
The library contains the following modules:
89-
90-
| Module | uProtocol Specification | Purpose |
91-
| --------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
92-
| transport | [uP-L1 Specifications](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.7/up-l1/README.adoc) | Implementation of MQTT5 uTransport client used for bidirectional point-2-point communication between uEs. |
93-
94-
### Supported Message Priority Levels
95-
`uman~utransport-send-ignore-priority~1`
96-
97-
The `Mqtt5Transport::send` function always uses a standard MQTT 5 PUBLISH packet to transfer the message to the MQTT broker regardless of the service class set on a uProtocol message.
98-
99-
Covers:
100-
- req~utransport-send-qos-mapping~1
101-
102-
### Supported Message Delivery Methods
103-
`uman~utransport-supported-message-delivery~1`
104-
105-
The MQTT 5 transport provided by this crate supports the [push delivery method](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.7/up-l1/README.adoc#5-message-delivery) only.
106-
The `Mqtt5Transport::receive` function therefore always returns `UCode::UNIMPLEMENTED`.
107-
108-
Covers:
109-
- `req~utransport-delivery-methods~1`
110-
111-
### Maximum number of listeners
112-
`uman~max-listeners-configuration~1`
113-
114-
The MQTT 5 transport provided by this crate can be configured with the maximum number of filter patterns that listeners can be registered for by means of the `MqttTransportOptions` struct that is being passed into `Mqtt5Transport::new`.
115-
Please refer to the [API Documentation](https://docs.rs/up-transport-mqtt5/) for details.
116-
117-
Covers:
118-
- `req~utransport-registerlistener-max-listeners~1`
119-
120-
### Known Limitations
121-
122-
The crate currently does not use the proper UCode to indicate authorization problems when sending messages or registering listeners.
123-
124-
## Design
125-
126-
### Message Priority Mapping
127-
`dsn~utransport-send-ignore-priority~1`
53+
```bash
54+
docker compose -f tests/mosquitto/docker-compose.yaml up --detach
55+
```
12856

129-
The MQTT 5 standard does not define any mechanism for message prioritization. All messages are being processed with the same priority.
130-
Consequently, the MQTT 5 transport provided by this crate simply ignores the service class set on a uProtocol message being sent.
57+
2. Run the Subscriber with options appropriate for your MQTT broker.
58+
When using the Mosquitto broker started via Docker Compose, then the defaults should work:
13159

132-
Covers:
133-
- `req~utransport-send-qos-mapping~1`
60+
```bash
61+
cargo run --example subscriber_example
62+
```
13463

135-
### Message Delivery
136-
`dsn~utransport-supported-message-delivery~1`
64+
The Subscriber also supports configuration via command line options and/or environment variables:
13765

138-
All messages are being received by means of subscribing to relevant topics on the MQTT broker and delivering the messages to listeners that have been registered via `up_rust::UTransport::register_listener`.
139-
The transport spawns a dedicated [tokio task](https://tokio.rs/tokio/tutorial/spawning#tasks) that listens for incoming messages and dispatches them to the registered listeners on the same thread that the message handling task runs on.
66+
```bash
67+
cargo run --example subscriber_example -- --help
68+
```
14069

141-
Rationale:
142-
The MQTT 5 standard does not provide means to poll the broker for messages but only supports the push model by means of clients subscribing to topic filters.
70+
3. Run the Publisher with options appropriate for your MQTT broker.
71+
When using the Mosquitto broker started via Docker Compose, then the defaults should work:
14372

144-
The transport requires a tokio runtime to execute but does not make any implicit assumption regarding the availability and size of thread pools. This provides for flexibility regarding the environment that the transport can be deployed to but also requires application developers to take care when implementing message listeners, making sure to not block the transport's incoming message handler when processing a dispatched message.
73+
```bash
74+
cargo run --example publisher_example
75+
```
14576

146-
Covers:
147-
- `req~utransport-delivery-methods~1`
77+
The Publisher also supports configuration via command line options and/or environment variables:
14878

149-
Needs: impl, utest
79+
```bash
80+
cargo run --example publisher_example -- --help
81+
```

src/lib.rs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
********************************************************************************/
1313

1414
/*!
15-
This crate provides an implementation of the MQTT 5 uProtocol Transport.
15+
This crate provides an implementation of the [uProtocol MQTT 5 Transport v1.6.0-alpha.7](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.7/up-l1/mqtt_5.adoc).
1616
1717
The transport requires an MQTT 5 broker to connect to and uses MQTT 5 `PUBLISH`
1818
packets to transfer uProtocol messages between uEntities.
@@ -27,8 +27,23 @@ processing requirements of the use case at hand. The transport does
2727
not make any implicit assumptions about the number of threads available
2828
and does not spawn any threads itself.
2929
30+
## Using the Library
31+
32+
Most developers will want to create an instance of the [Mqtt5Transport] struct and use it with uProtocol's Communication Layer API and its default implementation which are provided by the *up-rust* library. The crate also provides an implementation of [uProtocol's Transport & Session Layer API](up_rust::UTransport) which can be used to send and receive arbitrary (uProtocol) messages regardless of any message exchange patterns as imposed by the Communication Layer API.
33+
34+
The libraries need to be added to the `[dependencies]` section of the `Cargo.toml` file:
35+
36+
```toml
37+
[dependencies]
38+
up-rust = { version = "0.9" }
39+
up-transport-mqtt5 = { version = "0.4" }
40+
```
41+
42+
Please refer to the [examples](https://github.com/eclipse-uprotocol/up-transport-mqtt5-rust/tree/v0.4.0/examples) folder to see how to initialize and use the transport.
43+
3044
[tokio `Runtime`]: https://docs.rs/tokio/latest/tokio/runtime/index.html
3145
*/
46+
3247
use std::collections::HashSet;
3348
use std::sync::Arc;
3449

@@ -42,6 +57,8 @@ use mqtt_client::MqttClientOperations;
4257
pub use mqtt_client::{MqttClientOptions, SslOptions};
4358
use paho_mqtt::{self as mqtt, Message, QOS_1};
4459
use tokio::{sync::RwLock, task::JoinHandle};
60+
#[allow(unused_imports)]
61+
use up_rust::UTransport;
4562
use up_rust::{ComparableListener, UAttributes, UCode, UMessage, UStatus, UUri, UUriError};
4663

4764
mod listener_registry;
@@ -311,17 +328,50 @@ fn verify_authority_name<S: Into<String>>(authority: S) -> Result<String, UStatu
311328
///
312329
/// The transport spawns a dedicated tokio task that listens for incoming messages
313330
/// and dispatches them to the listeners that have been registered using
314-
/// `up_rust::UTransport::register_listener`.
331+
/// [up_rust::UTransport::register_listener].
315332
///
316333
/// <div class="warning">
317334
///
318335
/// The registered listeners are being invoked sequentially on the **same thread**
319336
/// that the message handling task runs on. Implementers of listeners are therefore
320337
/// **strongly advised** to move non-trivial processing logic to **another/dedicated
321-
/// thread**, if necessary. Please refer to the `subscriber_example` in the
322-
/// examples directory for how this could be done.
338+
/// thread**, if necessary. Please refer to the *subscriber_example* in the
339+
/// *examples* folder for how this could be done.
323340
///
324341
/// </div>
342+
///
343+
/// ### Supported Message Priority Levels
344+
///
345+
/// The [Self::send] function always uses a standard MQTT 5 PUBLISH packet to transfer the message
346+
/// to the MQTT broker regardless of the service class (priority) set on a uProtocol message.
347+
///
348+
/// ### Supported Message Delivery Methods
349+
///
350+
/// The transport supports the [push delivery method](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.7/up-l1/README.adoc#5-message-delivery) only.
351+
/// The [Self::receive] function therefore always returns [UCode::UNIMPLEMENTED].
352+
///
353+
/// ### Maximum number of listeners
354+
///
355+
/// The transport can be configured with the maximum number of filter patterns that listeners can
356+
/// be registered for by means of the [Mqtt5TransportOptions] struct that is being passed into [Self::new].
357+
///
358+
/// ### Authentication & Authorization
359+
///
360+
/// The transport can be configured with credentials that the transport will provide to the MQTT 5
361+
/// broker during connection establishment. A _username_ and _password_ and/or an X.509 client certificate
362+
/// can be specified as part of the [Mqtt5TransportOptions] that are passed into the [Self::new] function
363+
/// for creating a transport instance.
364+
///
365+
/// The mechanism for configuring access to topics is specific to the particular MQTT 5 broker at hand.
366+
/// The code in the [integration tests folder](https://github.com/eclipse-uprotocol/up-transport-mqtt5-rust/tree/v0.4.0/tests)
367+
/// illustrates, how ACLs can be used with an Eclipse Mosquitto MQTT broker to restrict a client's
368+
/// authority to publish messages and subscribe to topics.
369+
///
370+
// [uman->req~utransport-send-qos-mapping~1]
371+
// [uman->req~utransport-delivery-methods~1]
372+
// [uman->req~utransport-registerlistener-max-listeners~1]
373+
// [uman->req~utransport-send-prevent-address-spoofing~1]
374+
// [uman->req~utransport-registerlistener-prevent-unauthorized-access~1]
325375
pub struct Mqtt5Transport {
326376
/// Client instance for connecting to mqtt broker.
327377
mqtt_client: Arc<dyn MqttClientOperations>,
@@ -339,16 +389,20 @@ impl Mqtt5Transport {
339389
/// Creates a new transport.
340390
///
341391
/// The connection to the MQTT broker needs to be established by means of the
342-
/// [`Self::connect`] function. This allows for clients to implement any particular
392+
/// [Self::connect] function. This allows for clients to implement any particular
343393
/// connection strategy using e.g. an exponential backoff for subsequent connection
344394
/// attempts.
345395
///
346396
/// # Arguments
347397
/// * `options` - Configuration options for the transport.
348-
/// * `authority_name` - Authority name of the local uEntity.
398+
/// * `authority_name` - The (logical) name to use for the host that the local uEntity runs on.
399+
/// This name will be used as part of the origin and destination addresses of uProtocol messages.
400+
/// It is important to use a meaningful authority name here and make sure that it is unique across all
401+
/// uEntities in the same deployment, otherwise message filtering will not work correctly.
349402
///
350403
/// # Errors
351-
/// Will return an error if the creation of the Paho client fails, or if the given authority name is invalid.
404+
/// Will return an error if the creation of the underlying Paho MQTT client fails, or if the given authority
405+
/// name is invalid.
352406
pub async fn new<S: Into<String>>(
353407
options: Mqtt5TransportOptions,
354408
authority_name: S,

0 commit comments

Comments
 (0)