From 1e96245ca19ecd3bc837b9aae2a766cabce1cf69 Mon Sep 17 00:00:00 2001 From: Christian Fritz Date: Wed, 31 Mar 2021 10:05:06 -0700 Subject: [PATCH] Added a preUnsubscribe handler, analog to authorizeSubscribe Invoked then a client unsubscribes from topics. Just like authorizeSubscribe, this function allows modifying the topics because executing the unsubscribe. This is needed in cases where topics are modified in authorizeSubscribe. For example: ```js aedes.authorizeSubscribe = (client, subscription, callback) => { // overwrite subscription: force client to its namespace subscription.topic = `/${client.id}/${subscription.topic}`; callback(null, subscription); } aedes.preUnsubscribe = (client, packet, callback) => { // overwrite unsubscriptions: force client to its namespace for (let i in packet.unsubscriptions) { packet.unsubscriptions[i] = `/${client.id}/${packet.unsubscriptions[i]}`; } callback(client, packet) } ``` Includes fix-ups: - Update docs/Aedes.md Co-authored-by: Daniel Lando - added defaultPreUnsubscribe - made preUnsubscribe async like authorizeSubscribe --- aedes.js | 6 ++++++ docs/Aedes.md | 27 +++++++++++++++++++++++++++ lib/handlers/unsubscribe.js | 8 +++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/aedes.js b/aedes.js index 36fd056a..03408ad3 100644 --- a/aedes.js +++ b/aedes.js @@ -25,6 +25,7 @@ const defaultOptions = { authorizePublish: defaultAuthorizePublish, authorizeSubscribe: defaultAuthorizeSubscribe, authorizeForward: defaultAuthorizeForward, + preUnsubscribe: defaultPreUnsubscribe, published: defaultPublished, trustProxy: false, trustedProxies: [], @@ -69,6 +70,7 @@ function Aedes (opts) { this.authorizePublish = opts.authorizePublish this.authorizeSubscribe = opts.authorizeSubscribe this.authorizeForward = opts.authorizeForward + this.preUnsubscribe = opts.preUnsubscribe this.published = opts.published this.decodeProtocol = opts.decodeProtocol @@ -337,6 +339,10 @@ function defaultAuthorizeForward (client, packet) { return packet } +function defaultPreUnsubscribe (client, packet, callback) { + callback(client, packet) +} + function defaultPublished (packet, client, callback) { callback(null) } diff --git a/docs/Aedes.md b/docs/Aedes.md index afeaa0e8..887e4566 100644 --- a/docs/Aedes.md +++ b/docs/Aedes.md @@ -29,6 +29,7 @@ - [Handler: authorizePublish (client, packet, callback)](#handler-authorizepublish-client-packet-callback) - [Handler: authorizeSubscribe (client, subscription, callback)](#handler-authorizesubscribe-client-subscription-callback) - [Handler: authorizeForward (client, packet)](#handler-authorizeforward-client-packet) + - [Handler: preUnsubscribe (client, packet, callback)](#handler-preunsubscribe-client-packet-callback) - [Handler: published (packet, client, callback)](#handler-published-packet-client-callback) ## new Aedes([options]) / new Aedes.Server([options]) @@ -386,6 +387,31 @@ aedes.authorizeForward = function (client, packet) { } ``` +## Handler: preUnsubscribe (client, packet, callback) + +- client: [``](./Client.md) +- packet: `` & [`UNSUBSCRIBE`][UNSUBSCRIBE] +- callback: `` + +Invoked when a client unsubscribes from topics. Just like +[`authorizeSubscribe`](#handler-authorizesubscribe-client-subscription-callback), this function allows modifying the topics before executing the unsubscribe. This may be needed in cases where topics were modified in `authorizeSubscribe`. For example: + +```js +aedes.authorizeSubscribe = (client, subscription, callback) => { + // overwrite subscription: force client to its namespace + subscription.topic = `/${client.id}/${subscription.topic}`; + callback(null, subscription); +} + +aedes.preUnsubscribe = (client, packet, callback) => { + // overwrite unsubscriptions: force client to its namespace + for (let i in packet.unsubscriptions) { + packet.unsubscriptions[i] = `/${client.id}/${packet.unsubscriptions[i]}`; + } + callback(packet, client) +} +``` + ## Handler: published (packet, client, callback) - packet: `` & [`PUBLISH`][PUBLISH] @@ -397,6 +423,7 @@ same as [`Event: publish`](#event-publish), but provides a backpressure function [CONNECT]: https://github.com/mqttjs/mqtt-packet#connect [CONNACK]: https://github.com/mqttjs/mqtt-packet#connack [SUBSCRIBE]: https://github.com/mqttjs/mqtt-packet#subscribe +[UNSUBSCRIBE]: https://github.com/mqttjs/mqtt-packet#unsubscribe [PINGREQ]: https://github.com/mqttjs/mqtt-packet#pingreq [PUBLISH]: https://github.com/mqttjs/mqtt-packet#publish [PUBREL]: https://github.com/mqttjs/mqtt-packet#pubrel diff --git a/lib/handlers/unsubscribe.js b/lib/handlers/unsubscribe.js index de8b4cb7..55101d3d 100644 --- a/lib/handlers/unsubscribe.js +++ b/lib/handlers/unsubscribe.js @@ -14,8 +14,14 @@ function UnsubscribeState (client, packet, finish) { this.finish = finish } +function preUnsubscribe (client, packet, done) { + client.broker.preUnsubscribe(client, packet, (c, p) => + handleUnsubscribe(c, p, done)) +} + function handleUnsubscribe (client, packet, done) { const broker = client.broker + const unsubscriptions = packet.unsubscriptions let err @@ -101,4 +107,4 @@ function completeUnsubscribe (err) { function noop () {} -module.exports = handleUnsubscribe +module.exports = preUnsubscribe