mod_muc_http_auth: Allow for enabling/disabling per user host
IMPORTANT: This is a breaking change.
The `muc_http_auth_enabled_for` and `muc_http_auth_disabled_for` options are
now maps (with user hosts as keys) and not sets.
--- a/mod_muc_http_auth/README.md Mon Oct 25 12:40:26 2021 +0200
+++ b/mod_muc_http_auth/README.md Mon Oct 25 15:58:16 2021 +0200
@@ -1,12 +1,12 @@
# Introduction
-This module externalizes MUC authorization via HTTP.
+This module externalizes MUC authorization via HTTP.
Whenever a user wants to join a MUC, an HTTP GET request is made to `authorization_url`
-with the user's bare jid (`userJID`), the MUC jid (`mucJID`) and the user's nickname (`nickname`) as GET parameters.
-Example:
+with the user's bare jid (`userJID`), the MUC jid (`mucJID`) and the user's nickname (`nickname`) as GET parameters.
+Example:
`https://www.prosody.im/users/can-join/?userJID=romeo@example.com&mucJID=teaparty@chat.example.com&nickname=Romeo`
-This allows an external service to decide whether a user is authorized to join a MUC or not.
+This allows an external service to decide whether a user is authorized to join a MUC or not.
When a user is authorized to join a MUC, this module expects the following JSON payload:
```
@@ -39,26 +39,30 @@
## Settings
-|Name |Description |Default |
-|-----|------------|--------|
-|muc_http_auth_url| URL of the external HTTP service to which send `userJID`, `mucJID` and `nickname` in a GET request | "" |
-|muc_http_auth_enabled_for| List of MUC names (node part) to enable this module for | nil |
-|muc_http_auth_disabled_for| List of MUC names (node part) to disable this module for | nil |
-|muc_http_auth_insecure| Disable certificate verification for request. Only intended for development of the external service. | false |
-|muc_http_auth_authorization_header| Value of the Authorization header if requested by the external HTTP service. Example: `Basic dXNlcm5hbWU6cGFzc3dvcmQ=`| nil |
+| Name | Description | Default |
+|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|---------|
+| muc_http_auth_url | URL of the external HTTP service to which send `userJID`, `mucJID` and `nickname` in a GET request | "" |
+| muc_http_auth_enabled_for | A map of user hostnames to an array of MUC names (node part) to enable this module for. To enable for all hostnames, use `"all"` as key. | nil |
+| muc_http_auth_disabled_for | A map of user hostnames to an array of MUC names (node part) to disable this module for. To disable for all hostnames, use `"all"` as key. | nil |
+| muc_http_auth_insecure | Disable certificate verification for request. Only intended for development of the external service. | false |
+| muc_http_auth_authorization_header | Value of the Authorization header if requested by the external HTTP service. Example: `Basic dXNlcm5hbWU6cGFzc3dvcmQ=` | nil |
This module can be enabled/disabled for specific rooms. Only one of the following settings must be set.
```
--- muc_http_auth_enabled_for = {"teaparty"}
--- muc_http_auth_disabled_for = {"teaparty"}
+-- muc_http_auth_enabled_for = {["all"] = {"teaparty"}}
+-- muc_http_auth_disabled_for = {["all"] = {"teaparty"}}
```
If none is set, all rooms in the MUC component will have this module enabled.
-Note: Use the node part of the MUC jid for these lists. Example:
+Note: Use the node part of the MUC jid for these lists. Example:
Wrong:
-`muc_http_auth_enabled_for = {"teaparty@rooms.example.net"}`
+`muc_http_auth_enabled_for = {["all"] = {"teaparty@rooms.example.net"}}`
Correct:
-`muc_http_auth_enabled_for = {"teaparty"}`
\ No newline at end of file
+`muc_http_auth_enabled_for = {["all"] = {"teaparty"}}`
+
+It's also possible to disable/enable checking for a particular host, for example:
+
+ `muc_http_auth_enabled_for = {["jabber.org"] = {"teaparty"}, ["prosody.org] = {"orchard"}}`
--- a/mod_muc_http_auth/mod_muc_http_auth.lua Mon Oct 25 12:40:26 2021 +0200
+++ b/mod_muc_http_auth/mod_muc_http_auth.lua Mon Oct 25 15:58:16 2021 +0200
@@ -1,15 +1,16 @@
-local wait_for = require "util.async".wait_for;
local http = require "net.http";
+local jid_bare = require "util.jid".bare;
+local jid_host = require "util.jid".host;
+local jid_node = require "util.jid".node;
+local jid_resource = require "util.jid".resource;
local json = require "util.json";
local st = require "util.stanza";
-local jid_node = require "util.jid".node;
-local jid_bare = require "util.jid".bare;
-local jid_resource = require "util.jid".resource;
local urlencode = require "util.http".urlencode;
+local wait_for = require "util.async".wait_for;
local authorization_url = module:get_option("muc_http_auth_url", "")
-local enabled_for = module:get_option_set("muc_http_auth_enabled_for", nil)
-local disabled_for = module:get_option_set("muc_http_auth_disabled_for", nil)
+local enabled_for = module:get_option("muc_http_auth_enabled_for", nil)
+local disabled_for = module:get_option("muc_http_auth_disabled_for", nil)
local insecure = module:get_option("muc_http_auth_insecure", false) --For development purposes
local authorize_registration = module:get_option("muc_http_auth_authorize_registration", false)
local authorization_header = module:get_option("muc_http_auth_authorization_header", nil)
@@ -21,12 +22,21 @@
local verbs = {presence='join', iq='register'};
-local function must_be_authorized(room_node)
+local function must_be_authorized(room_node, user_host)
-- If none of these is set, all rooms need authorization
if not enabled_for and not disabled_for then return true; end
- if enabled_for then return enabled_for:contains(room_node); end
- if disabled_for then return not disabled_for:contains(room_node); end
+ if enabled_for then
+ local enabled_for_host = set.new(enabled_for[user_host] or {});
+ local enabled_for_all = set.new(enabled_for['all'] or {});
+ return enabled_for_host:contains(room_node) or enabled_for_all:contains(room_node);
+
+ end
+ if disabled_for then
+ local disabled_for_host = set.new(disabled_for[user_host] or {});
+ local disabled_for_all = set.new(disabled_for['all'] or {});
+ return not disabled_for_host:contains(room_node) and not disabled_for_all:contains(room_node);
+ end
end
local function handle_success(response)
@@ -50,9 +60,12 @@
local room, origin = event.room, event.origin;
if (not room) or (not origin) then return; end
- if not must_be_authorized(jid_node(room.jid)) then return; end
+ local user_bare_jid = jid_bare(stanza.attr.from)
+ if not must_be_authorized(jid_node(room.jid), jid_host(user_bare_jid)) then
+ module:log("debug", "Authorization not required for "..jid_node(room.jid).." and "..jid_host(user_bare_jid))
+ return;
+ end
- local user_bare_jid = jid_bare(stanza.attr.from);
local user_nickname = jid_resource(stanza.attr.to);
-- Nickname is mandatory to enter a MUC