Software /
code /
prosody-modules
File
mod_saslauth_muc/mod_saslauth_muc.lua @ 5418:f2c7bb3af600
mod_http_oauth2: Add role selector to consent page
List includes all roles available to the user, if more than one.
Defaults to either the first role in the scope string or the users
primary role.
Earlier draft listed all roles, but having options that can't be
selected is bad UX and the entire list of all roles on the server could
be long, and perhaps even sensitive.
Allows e.g. picking a role with fewer permissions than what might
otherwise have been selected.
UX wise, doing this with more checkboxes or possibly radio buttons would
have been confusion and/or looked messier.
Fixes the previous situation where unselecting a role would default to
the primary role, which could be more permissions than requested.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 05 May 2023 01:23:13 +0200 |
parent | 3072:926db29176f5 |
line wrap: on
line source
-- -- mod_saslauth_muc -- This module implements http://xmpp.org/extensions/inbox/remote-auth.html for Prosody's MUC component -- -- In your config: -- Component "conference.example.com" "muc" -- modules_enabled = { "saslauth_muc" }; -- -- local timeout = 60; -- SASL timeout in seconds -- various imports local new_sasl = require "util.sasl".new; local st = require "util.stanza"; local timer = require "util.timer"; local jid_bare = require "util.jid".bare; local jid_prep = require "util.jid".prep; local base64 = require "util.encodings".base64; local hosts = hosts; local module = module; local pairs, next = pairs, next; local os_time = os.time; local muc_password = module:require("muc/password"); -- SASL sessions management local _rooms = {}; -- SASL data local function get_handler_for(room, jid) return _rooms[room] and _rooms[room][jid]; end local function remove_handler_for(room, jid) if _rooms[room] then _rooms[room][jid] = nil; end end local function create_handler_for(room_jid, jid) _rooms[room_jid] = _rooms[room_jid] or {}; _rooms[room_jid][jid] = new_sasl(module.host, { plain = function(sasl, username, realm) local muc = hosts[module.host].modules.muc; local room = muc and muc.get_room_from_jid(room_jid); local password = room and muc_password.get(room); local ret = password and true or nil; return password or "", ret; end }); _rooms[room_jid][jid].timeout = os_time() + timeout; return _rooms[room_jid][jid]; end -- Timer to clear SASL sessions timer.add_task(timeout, function(now) for room, handlers in pairs(_rooms) do for jid, handler in pairs(handlers) do if handler.timeout <= now then handlers[jid] = nil; end end if next(handlers) == nil then _rooms[room] = nil; end end return timeout; end); function module.unload() timeout = nil; -- stop timer on unload end -- Stanza handlers -- Don't allow anyone to join room unless they provide the password module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; local room_password = muc_password.get(room); if room_password then -- room has a password local x = stanza:get_child("x", "http://jabber.org/protocol/muc"); local password = x and x:get_child_text("password", "http://jabber.org/protocol/muc"); if not password then -- no password sent local sasl_handler = get_handler_for(jid_bare(stanza.attr.to), stanza.attr.from); if x and sasl_handler and sasl_handler.authorized then -- if already passed SASL x:reset():tag("password", { xmlns = "http://jabber.org/protocol/muc" }):text(room_password); else event.origin.send(st.error_reply(stanza, "auth", "not-authorized") :tag("sasl-required", { xmlns = "urn:xmpp:errors" })); return true; end end end end, -18); module:hook("iq-get/bare/urn:ietf:params:xml:ns:xmpp-sasl:mechanisms", function(event) local origin, stanza = event.origin, event.stanza; local reply = st.reply(stanza):tag("mechanisms", { xmlns='urn:ietf:params:xml:ns:xmpp-sasl' }); for mechanism in pairs(create_handler_for(stanza.attr.to, true):mechanisms()) do reply:tag("mechanism"):text(mechanism):up(); end origin.send(reply:up()); return true; end); local function build_reply(stanza, status, ret, err_msg) local reply = st.stanza(status, {xmlns = "urn:ietf:params:xml:ns:xmpp-sasl"}); if status == "challenge" then reply:text(base64.encode(ret or "")); elseif status == "failure" then reply:tag(ret):up(); if err_msg then reply:tag("text"):text(err_msg); end elseif status == "success" then reply:text(base64.encode(ret or "")); else module:log("error", "Unknown sasl status: %s", status); end return st.reply(stanza):add_child(reply); end local function handle_status(stanza, status) if status == "failure" then remove_handler_for(stanza.attr.to, stanza.attr.from); elseif status == "success" then get_handler_for(stanza.attr.to, stanza.attr.from).authorized = true; end end local function sasl_process_cdata(session, stanza) local text = stanza.tags[1][1]; if text then text = base64.decode(text); if not text then remove_handler_for(stanza.attr.to, stanza.attr.from); session.send(build_reply(stanza, "failure", "incorrect-encoding")); return true; end end local status, ret, err_msg = get_handler_for(stanza.attr.to, stanza.attr.from):process(text); handle_status(stanza, status); local s = build_reply(stanza, status, ret, err_msg); session.send(s); return true; end module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:auth", function(event) local session, stanza = event.origin, event.stanza; if not create_handler_for(stanza.attr.to, stanza.attr.from):select(stanza.tags[1].attr.mechanism) then remove_handler_for(stanza.attr.to, stanza.attr.from); session.send(build_reply(stanza, "failure", "invalid-mechanism")); return true; end return sasl_process_cdata(session, stanza); end); module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:response", function(event) local session, stanza = event.origin, event.stanza; if not get_handler_for(stanza.attr.to, stanza.attr.from) then session.send(build_reply(stanza, "failure", "not-authorized", "Out of order SASL element")); return true; end return sasl_process_cdata(session, event.stanza); end); module:hook("iq-set/bare/urn:ietf:params:xml:ns:xmpp-sasl:abort", function(event) local session, stanza = event.origin, event.stanza; remove_handler_for(stanza.attr.to, stanza.attr.from); session.send(build_reply(stanza, "failure", "aborted")); return true; end);