From 9f34801aba7249016e5471ecdeb1b4823ad9d38e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Mar 2022 10:19:35 +0000 Subject: util.logger: Support for removing individual log sinks without a full reset --- util/logger.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'util') diff --git a/util/logger.lua b/util/logger.lua index 20a5cef2..a94a1ee1 100644 --- a/util/logger.lua +++ b/util/logger.lua @@ -10,6 +10,7 @@ local pairs = pairs; local ipairs = ipairs; local require = require; +local t_remove = table.remove; local _ENV = nil; -- luacheck: std none @@ -80,6 +81,19 @@ local function add_simple_sink(simple_sink_function, levels) end end +local function remove_sink(sink_function) + local removed; + for level, sinks in pairs(level_sinks) do + for i = #sinks, 1, -1 do + if sinks[i] == sink_function then + t_remove(sinks, i); + removed = true; + end + end + end + return removed; +end + return { init = init; make_logger = make_logger; @@ -87,4 +101,5 @@ return { add_level_sink = add_level_sink; add_simple_sink = add_simple_sink; new = make_logger; + remove_sink = remove_sink; }; -- cgit v1.2.3 From a946bdf4ae58e6aa2dea7915a5da67099a6e5c63 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Mar 2022 10:20:23 +0000 Subject: util.logger: Return sink_function from add_simple_sink() This allows a simple sink to be later removed via remove_sink() --- util/logger.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/logger.lua b/util/logger.lua index a94a1ee1..148b98dc 100644 --- a/util/logger.lua +++ b/util/logger.lua @@ -79,6 +79,7 @@ local function add_simple_sink(simple_sink_function, levels) for _, level in ipairs(levels or {"debug", "info", "warn", "error"}) do add_level_sink(level, sink_function); end + return sink_function; end local function remove_sink(sink_function) -- cgit v1.2.3 From b617c24af4e1cc79fd27ed3dc852d6b9e53be543 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Mar 2022 10:21:43 +0000 Subject: util.prosodyctl.shell: Support for receiving partial lines (no automatic \n) --- util/prosodyctl/shell.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index bce27b94..0b1dd3f9 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -89,11 +89,15 @@ local function start(arg) --luacheck: ignore 212/arg local errors = 0; -- TODO This is weird, but works for now. client.events.add_handler("received", function(stanza) if stanza.name == "repl-output" or stanza.name == "repl-result" then + local dest = io.stdout; if stanza.attr.type == "error" then errors = errors + 1; - io.stderr:write(stanza:get_text(), "\n"); + dest = io.stderr; + end + if stanza.attr.eol == "0" then + dest:write(stanza:get_text()); else - print(stanza:get_text()); + dest:write(stanza:get_text(), "\n"); end end if stanza.name == "repl-result" then -- cgit v1.2.3 From 22cc92f4376b5a641cdbb337d34b40312972b1e8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 18 Mar 2022 15:22:00 +0000 Subject: util.array: Take advantage of table.move() --- util/array.lua | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'util') diff --git a/util/array.lua b/util/array.lua index c33a5ef1..9d438940 100644 --- a/util/array.lua +++ b/util/array.lua @@ -8,6 +8,7 @@ local t_insert, t_sort, t_remove, t_concat = table.insert, table.sort, table.remove, table.concat; +local t_move = require "util.table".move; local setmetatable = setmetatable; local getmetatable = getmetatable; @@ -137,13 +138,11 @@ function array_base.slice(outa, ina, i, j) return outa; end - for idx = 1, 1+j-i do - outa[idx] = ina[i+(idx-1)]; - end + + t_move(ina, i, j, 1, outa); if ina == outa then - for idx = 2+j-i, #outa do - outa[idx] = nil; - end + -- Clear (nil) remainder of range + t_move(ina, #outa+1, #outa*2, 2+j-i, ina); end return outa; end @@ -209,10 +208,7 @@ function array_methods:shuffle() end function array_methods:append(ina) - local len, len2 = #self, #ina; - for i = 1, len2 do - self[len+i] = ina[i]; - end + t_move(ina, 1, #ina, #self+1, self); return self; end -- cgit v1.2.3 From ceea9b1788ea9f06314c6aeb493c3b63c7ca6c5e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 18 Mar 2022 16:39:48 +0100 Subject: util.stanza: Use table.move in clone Code reduction, potentially a performance gain. --- util/stanza.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index a38f80b3..9e249059 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -21,6 +21,7 @@ local type = type; local s_gsub = string.gsub; local s_sub = string.sub; local s_find = string.find; +local t_move = table.move or require "util.table".move; local valid_utf8 = require "util.encodings".utf8.valid; @@ -283,17 +284,13 @@ local function _clone(stanza, only_top) for k,v in pairs(old_namespaces) do namespaces[k] = v; end end local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; + setmetatable(new, stanza_mt); if not only_top then - for i=1,#stanza do - local child = stanza[i]; - if child.name then - child = _clone(child); - t_insert(tags, child); - end - t_insert(new, child); - end + t_move(stanza, 1, #stanza, 1, new); + t_move(stanza.tags, 1, #stanza.tags, 1, tags); + new:maptags(_clone); end - return setmetatable(new, stanza_mt); + return new; end local function clone(stanza, only_top) -- cgit v1.2.3 From 47dbb928887f1ece83ca7005eb714a4e937113e5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 18 Mar 2022 16:43:06 +0100 Subject: util.stanza: Create tables with correct size to avoid reallocations Potential performance gain since the tables don't need to be resized as they grow to the final size. --- util/stanza.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 9e249059..a14be5f0 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -22,6 +22,7 @@ local s_gsub = string.gsub; local s_sub = string.sub; local s_find = string.find; local t_move = table.move or require "util.table".move; +local t_create = require"util.table".create; local valid_utf8 = require "util.encodings".utf8.valid; @@ -276,14 +277,26 @@ function stanza_mt:find(path) end local function _clone(stanza, only_top) - local attr, tags = {}, {}; + local attr = {}; for k,v in pairs(stanza.attr) do attr[k] = v; end local old_namespaces, namespaces = stanza.namespaces; if old_namespaces then namespaces = {}; for k,v in pairs(old_namespaces) do namespaces[k] = v; end end - local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; + local tags, new; + if only_top then + tags = {}; + new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; + else + tags = t_create(#stanza.tags, 0); + new = t_create(#stanza, 4); + new.name = stanza.name; + new.attr = attr; + new.namespaces = namespaces; + new.tags = tags; + end + setmetatable(new, stanza_mt); if not only_top then t_move(stanza, 1, #stanza, 1, new); -- cgit v1.2.3 From 38346dd6f1dcd963e17722bf175445465d7683f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Wed, 27 Apr 2022 17:44:14 +0200 Subject: net: isolate LuaSec-specifics For this, various accessor functions are now provided directly on the sockets, which reach down into the LuaSec implementation to obtain the information. While this may seem of little gain at first, it hides the implementation detail of the LuaSec+LuaSocket combination that the actual socket and the TLS layer are separate objects. The net gain here is that an alternative implementation does not have to emulate that specific implementation detail and "only" has to expose LuaSec-compatible data structures on the new functions. --- util/sslconfig.lua | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/sslconfig.lua b/util/sslconfig.lua index 6074a1fb..23fb934c 100644 --- a/util/sslconfig.lua +++ b/util/sslconfig.lua @@ -3,9 +3,16 @@ local type = type; local pairs = pairs; local rawset = rawset; +local rawget = rawget; +local error = error; local t_concat = table.concat; local t_insert = table.insert; local setmetatable = setmetatable; +local config_path = prosody.paths.config or "."; +local resolve_path = require"util.paths".resolve_relative_path; + +-- TODO: use net.server directly here +local tls_impl = require"net.tls_luasec"; local _ENV = nil; -- luacheck: std none @@ -34,7 +41,7 @@ function handlers.options(config, field, new) options[value] = true; end end - config[field] = options; + rawset(config, field, options) end handlers.verifyext = handlers.options; @@ -70,6 +77,20 @@ finalisers.curveslist = finalisers.ciphers; -- TLS 1.3 ciphers finalisers.ciphersuites = finalisers.ciphers; +-- Path expansion +function finalisers.key(path) + if type(path) == "string" then + return resolve_path(config_path, path); + else + return nil + end +end +finalisers.certificate = finalisers.key; +finalisers.cafile = finalisers.key; +finalisers.capath = finalisers.key; +-- XXX: copied from core/certmanager.lua, but this seems odd, because it would remove a dhparam function from the config +finalisers.dhparam = finalisers.key; + -- protocol = "x" should enable only that protocol -- protocol = "x+" should enable x and later versions @@ -89,11 +110,14 @@ end -- Merge options from 'new' config into 'config' local function apply(config, new) + -- 0 == cache + rawset(config, 0, nil); if type(new) == "table" then for field, value in pairs(new) do (handlers[field] or rawset)(config, field, value); end end + return config end -- Finalize the config into the form LuaSec expects @@ -107,17 +131,45 @@ local function final(config) return output; end +local function build(config) + local cached = rawget(config, 0); + if cached then + return cached, nil + end + + local ctx, err = tls_impl.new_context(config:final(), config); + if ctx then + rawset(config, 0, ctx); + end + return ctx, err +end + local sslopts_mt = { __index = { apply = apply; final = final; + build = build; }; + __newindex = function() + error("SSL config objects cannot be modified directly. Use :apply()") + end; }; + local function new() return setmetatable({options={}}, sslopts_mt); end +local function clone(config) + local result = new(); + for k, v in pairs(config) do + rawset(result, k, v); + end + return result +end + +sslopts_mt.__index.clone = clone; + return { apply = apply; final = final; -- cgit v1.2.3 From 9f7c3b9ba6c2fde4431cd6f3a12072518b478d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sat, 2 Apr 2022 11:15:33 +0200 Subject: net: refactor sslconfig to not depend on LuaSec This now requires that the network backend exposes a tls_builder function, which essentially wraps the former util.sslconfig.new() function, passing a factory to create the eventual SSL context. That allows a net.server backend to pick whatever it likes as SSL context factory, as long as it understands the config table passed by the SSL config builder. Heck, a backend could even mock and replace the entire SSL config builder API. --- util/sslconfig.lua | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'util') diff --git a/util/sslconfig.lua b/util/sslconfig.lua index 23fb934c..0078365b 100644 --- a/util/sslconfig.lua +++ b/util/sslconfig.lua @@ -8,12 +8,8 @@ local error = error; local t_concat = table.concat; local t_insert = table.insert; local setmetatable = setmetatable; -local config_path = prosody.paths.config or "."; local resolve_path = require"util.paths".resolve_relative_path; --- TODO: use net.server directly here -local tls_impl = require"net.tls_luasec"; - local _ENV = nil; -- luacheck: std none @@ -78,9 +74,9 @@ finalisers.curveslist = finalisers.ciphers; finalisers.ciphersuites = finalisers.ciphers; -- Path expansion -function finalisers.key(path) +function finalisers.key(path, config) if type(path) == "string" then - return resolve_path(config_path, path); + return resolve_path(config._basedir, path); else return nil end @@ -110,11 +106,13 @@ end -- Merge options from 'new' config into 'config' local function apply(config, new) - -- 0 == cache - rawset(config, 0, nil); + rawset(config, "_cache", nil); if type(new) == "table" then for field, value in pairs(new) do - (handlers[field] or rawset)(config, field, value); + -- exclude keys which are internal to the config builder + if field:sub(1, 1) ~= "_" then + (handlers[field] or rawset)(config, field, value); + end end end return config @@ -124,7 +122,10 @@ end local function final(config) local output = { }; for field, value in pairs(config) do - output[field] = (finalisers[field] or id)(value); + -- exclude keys which are internal to the config builder + if field:sub(1, 1) ~= "_" then + output[field] = (finalisers[field] or id)(value, config); + end end -- Need to handle protocols last because it adds to the options list protocol(output); @@ -132,14 +133,14 @@ local function final(config) end local function build(config) - local cached = rawget(config, 0); + local cached = rawget(config, "_cache"); if cached then return cached, nil end - local ctx, err = tls_impl.new_context(config:final(), config); + local ctx, err = rawget(config, "_context_factory")(config:final(), config); if ctx then - rawset(config, 0, ctx); + rawset(config, "_cache", ctx); end return ctx, err end @@ -156,13 +157,21 @@ local sslopts_mt = { }; -local function new() - return setmetatable({options={}}, sslopts_mt); +-- passing basedir through everything is required to avoid sslconfig depending +-- on prosody.paths.config +local function new(context_factory, basedir) + return setmetatable({ + _context_factory = context_factory, + _basedir = basedir, + options={}, + }, sslopts_mt); end local function clone(config) local result = new(); for k, v in pairs(config) do + -- note that we *do* copy the internal keys on clone -- we have to carry + -- both the factory and the cache with us rawset(result, k, v); end return result @@ -173,5 +182,5 @@ sslopts_mt.__index.clone = clone; return { apply = apply; final = final; - new = new; + _new = new; }; -- cgit v1.2.3 From 0267554c8e91555c658241861943b684e5f98000 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 30 May 2022 15:28:44 +0200 Subject: prosodyctl shell: Communicate width of terminal to mod_admin_shell This lets it adjust the width of tables to the actual terminal width. --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 0b1dd3f9..cad9ac00 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -27,7 +27,7 @@ local function read_line(prompt_string) end local function send_line(client, line) - client.send(st.stanza("repl-input"):text(line)); + client.send(st.stanza("repl-input", { width = os.getenv "COLUMNS" }):text(line)); end local function repl(client) -- cgit v1.2.3 From fb0299bbf610cf757f9821c358e5c003016643c7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 30 May 2022 16:25:35 +0200 Subject: util.vcard: Delete since nothing uses it --- util/vcard.lua | 574 --------------------------------------------------------- 1 file changed, 574 deletions(-) delete mode 100644 util/vcard.lua (limited to 'util') diff --git a/util/vcard.lua b/util/vcard.lua deleted file mode 100644 index e311f73f..00000000 --- a/util/vcard.lua +++ /dev/null @@ -1,574 +0,0 @@ --- Copyright (C) 2011-2014 Kim Alvefur --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - --- TODO --- Fix folding. - -local st = require "util.stanza"; -local t_insert, t_concat = table.insert, table.concat; -local type = type; -local pairs, ipairs = pairs, ipairs; - -local from_text, to_text, from_xep54, to_xep54; - -local line_sep = "\n"; - -local vCard_dtd; -- See end of file -local vCard4_dtd; - -local function vCard_esc(s) - return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n"); -end - -local function vCard_unesc(s) - return s:gsub("\\?[\\nt:;,]", { - ["\\\\"] = "\\", - ["\\n"] = "\n", - ["\\r"] = "\r", - ["\\t"] = "\t", - ["\\:"] = ":", -- FIXME Shouldn't need to escape : in values, just params - ["\\;"] = ";", - ["\\,"] = ",", - [":"] = "\29", - [";"] = "\30", - [","] = "\31", - }); -end - -local function item_to_xep54(item) - local t = st.stanza(item.name, { xmlns = "vcard-temp" }); - - local prop_def = vCard_dtd[item.name]; - if prop_def == "text" then - t:text(item[1]); - elseif type(prop_def) == "table" then - if prop_def.types and item.TYPE then - if type(item.TYPE) == "table" then - for _,v in pairs(prop_def.types) do - for _,typ in pairs(item.TYPE) do - if typ:upper() == v then - t:tag(v):up(); - break; - end - end - end - else - t:tag(item.TYPE:upper()):up(); - end - end - - if prop_def.props then - for _,prop in pairs(prop_def.props) do - if item[prop] then - for _, v in ipairs(item[prop]) do - t:text_tag(prop, v); - end - end - end - end - - if prop_def.value then - t:text_tag(prop_def.value, item[1]); - elseif prop_def.values then - local prop_def_values = prop_def.values; - local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values]; - for i=1,#item do - t:text_tag(prop_def.values[i] or repeat_last, item[i]); - end - end - end - - return t; -end - -local function vcard_to_xep54(vCard) - local t = st.stanza("vCard", { xmlns = "vcard-temp" }); - for i=1,#vCard do - t:add_child(item_to_xep54(vCard[i])); - end - return t; -end - -function to_xep54(vCards) - if not vCards[1] or vCards[1].name then - return vcard_to_xep54(vCards) - else - local t = st.stanza("xCard", { xmlns = "vcard-temp" }); - for i=1,#vCards do - t:add_child(vcard_to_xep54(vCards[i])); - end - return t; - end -end - -function from_text(data) - data = data -- unfold and remove empty lines - :gsub("\r\n","\n") - :gsub("\n ", "") - :gsub("\n\n+","\n"); - local vCards = {}; - local current; - for line in data:gmatch("[^\n]+") do - line = vCard_unesc(line); - local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); - value = value:gsub("\29",":"); - if #params > 0 then - local _params = {}; - for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do - k = k:upper(); - local _vt = {}; - for _p in v:gmatch("[^\31]+") do - _vt[#_vt+1]=_p - _vt[_p]=true; - end - if isval == "=" then - _params[k]=_vt; - else - _params[k]=true; - end - end - params = _params; - end - if name == "BEGIN" and value == "VCARD" then - current = {}; - vCards[#vCards+1] = current; - elseif name == "END" and value == "VCARD" then - current = nil; - elseif current and vCard_dtd[name] then - local dtd = vCard_dtd[name]; - local item = { name = name }; - t_insert(current, item); - local up = current; - current = item; - if dtd.types then - for _, t in ipairs(dtd.types) do - t = t:lower(); - if ( params.TYPE and params.TYPE[t] == true) - or params[t] == true then - current.TYPE=t; - end - end - end - if dtd.props then - for _, p in ipairs(dtd.props) do - if params[p] then - if params[p] == true then - current[p]=true; - else - for _, prop in ipairs(params[p]) do - current[p]=prop; - end - end - end - end - end - if dtd == "text" or dtd.value then - t_insert(current, value); - elseif dtd.values then - for p in ("\30"..value):gmatch("\30([^\30]*)") do - t_insert(current, p); - end - end - current = up; - end - end - return vCards; -end - -local function item_to_text(item) - local value = {}; - for i=1,#item do - value[i] = vCard_esc(item[i]); - end - value = t_concat(value, ";"); - - local params = ""; - for k,v in pairs(item) do - if type(k) == "string" and k ~= "name" then - params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v); - end - end - - return ("%s%s:%s"):format(item.name, params, value) -end - -local function vcard_to_text(vcard) - local t={}; - t_insert(t, "BEGIN:VCARD") - for i=1,#vcard do - t_insert(t, item_to_text(vcard[i])); - end - t_insert(t, "END:VCARD") - return t_concat(t, line_sep); -end - -function to_text(vCards) - if vCards[1] and vCards[1].name then - return vcard_to_text(vCards) - else - local t = {}; - for i=1,#vCards do - t[i]=vcard_to_text(vCards[i]); - end - return t_concat(t, line_sep); - end -end - -local function from_xep54_item(item) - local prop_name = item.name; - local prop_def = vCard_dtd[prop_name]; - - local prop = { name = prop_name }; - - if prop_def == "text" then - prop[1] = item:get_text(); - elseif type(prop_def) == "table" then - if prop_def.value then --single item - prop[1] = item:get_child_text(prop_def.value) or ""; - elseif prop_def.values then --array - local value_names = prop_def.values; - if value_names.behaviour == "repeat-last" then - for i=1,#item.tags do - t_insert(prop, item.tags[i]:get_text() or ""); - end - else - for i=1,#value_names do - t_insert(prop, item:get_child_text(value_names[i]) or ""); - end - end - elseif prop_def.names then - local names = prop_def.names; - for i=1,#names do - if item:get_child(names[i]) then - prop[1] = names[i]; - break; - end - end - end - - if prop_def.props_verbatim then - for k,v in pairs(prop_def.props_verbatim) do - prop[k] = v; - end - end - - if prop_def.types then - local types = prop_def.types; - prop.TYPE = {}; - for i=1,#types do - if item:get_child(types[i]) then - t_insert(prop.TYPE, types[i]:lower()); - end - end - if #prop.TYPE == 0 then - prop.TYPE = nil; - end - end - - -- A key-value pair, within a key-value pair? - if prop_def.props then - local params = prop_def.props; - for i=1,#params do - local name = params[i] - local data = item:get_child_text(name); - if data then - prop[name] = prop[name] or {}; - t_insert(prop[name], data); - end - end - end - else - return nil - end - - return prop; -end - -local function from_xep54_vCard(vCard) - local tags = vCard.tags; - local t = {}; - for i=1,#tags do - t_insert(t, from_xep54_item(tags[i])); - end - return t -end - -function from_xep54(vCard) - if vCard.attr.xmlns ~= "vcard-temp" then - return nil, "wrong-xmlns"; - end - if vCard.name == "xCard" then -- A collection of vCards - local t = {}; - local vCards = vCard.tags; - for i=1,#vCards do - t[i] = from_xep54_vCard(vCards[i]); - end - return t - elseif vCard.name == "vCard" then -- A single vCard - return from_xep54_vCard(vCard) - end -end - -local vcard4 = { } - -function vcard4:text(node, params, value) -- luacheck: ignore 212/params - self:tag(node:lower()) - -- FIXME params - if type(value) == "string" then - self:text_tag("text", value); - elseif vcard4[node] then - vcard4[node](value); - end - self:up(); -end - -function vcard4.N(value) - for i, k in ipairs(vCard_dtd.N.values) do - value:text_tag(k, value[i]); - end -end - -local xmlns_vcard4 = "urn:ietf:params:xml:ns:vcard-4.0" - -local function item_to_vcard4(item) - local typ = item.name:lower(); - local t = st.stanza(typ, { xmlns = xmlns_vcard4 }); - - local prop_def = vCard4_dtd[typ]; - if prop_def == "text" then - t:text_tag("text", item[1]); - elseif prop_def == "uri" then - if item.ENCODING and item.ENCODING[1] == 'b' then - t:text_tag("uri", "data:;base64," .. item[1]); - else - t:text_tag("uri", item[1]); - end - elseif type(prop_def) == "table" then - if prop_def.values then - for i, v in ipairs(prop_def.values) do - t:text_tag(v:lower(), item[i]); - end - else - t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) - end - else - t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) - end - return t; -end - -local function vcard_to_vcard4xml(vCard) - local t = st.stanza("vcard", { xmlns = xmlns_vcard4 }); - for i=1,#vCard do - t:add_child(item_to_vcard4(vCard[i])); - end - return t; -end - -local function vcards_to_vcard4xml(vCards) - if not vCards[1] or vCards[1].name then - return vcard_to_vcard4xml(vCards) - else - local t = st.stanza("vcards", { xmlns = xmlns_vcard4 }); - for i=1,#vCards do - t:add_child(vcard_to_vcard4xml(vCards[i])); - end - return t; - end -end - --- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd -vCard_dtd = { - VERSION = "text", --MUST be 3.0, so parsing is redundant - FN = "text", - N = { - values = { - "FAMILY", - "GIVEN", - "MIDDLE", - "PREFIX", - "SUFFIX", - }, - }, - NICKNAME = "text", - PHOTO = { - props_verbatim = { ENCODING = { "b" } }, - props = { "TYPE" }, - value = "BINVAL", --{ "EXTVAL", }, - }, - BDAY = "text", - ADR = { - types = { - "HOME", - "WORK", - "POSTAL", - "PARCEL", - "DOM", - "INTL", - "PREF", - }, - values = { - "POBOX", - "EXTADD", - "STREET", - "LOCALITY", - "REGION", - "PCODE", - "CTRY", - } - }, - LABEL = { - types = { - "HOME", - "WORK", - "POSTAL", - "PARCEL", - "DOM", - "INTL", - "PREF", - }, - value = "LINE", - }, - TEL = { - types = { - "HOME", - "WORK", - "VOICE", - "FAX", - "PAGER", - "MSG", - "CELL", - "VIDEO", - "BBS", - "MODEM", - "ISDN", - "PCS", - "PREF", - }, - value = "NUMBER", - }, - EMAIL = { - types = { - "HOME", - "WORK", - "INTERNET", - "PREF", - "X400", - }, - value = "USERID", - }, - JABBERID = "text", - MAILER = "text", - TZ = "text", - GEO = { - values = { - "LAT", - "LON", - }, - }, - TITLE = "text", - ROLE = "text", - LOGO = "copy of PHOTO", - AGENT = "text", - ORG = { - values = { - behaviour = "repeat-last", - "ORGNAME", - "ORGUNIT", - } - }, - CATEGORIES = { - values = "KEYWORD", - }, - NOTE = "text", - PRODID = "text", - REV = "text", - SORTSTRING = "text", - SOUND = "copy of PHOTO", - UID = "text", - URL = "text", - CLASS = { - names = { -- The item.name is the value if it's one of these. - "PUBLIC", - "PRIVATE", - "CONFIDENTIAL", - }, - }, - KEY = { - props = { "TYPE" }, - value = "CRED", - }, - DESC = "text", -}; -vCard_dtd.LOGO = vCard_dtd.PHOTO; -vCard_dtd.SOUND = vCard_dtd.PHOTO; - -vCard4_dtd = { - source = "uri", - kind = "text", - xml = "text", - fn = "text", - n = { - values = { - "family", - "given", - "middle", - "prefix", - "suffix", - }, - }, - nickname = "text", - photo = "uri", - bday = "date-and-or-time", - anniversary = "date-and-or-time", - gender = "text", - adr = { - values = { - "pobox", - "ext", - "street", - "locality", - "region", - "code", - "country", - } - }, - tel = "text", - email = "text", - impp = "uri", - lang = "language-tag", - tz = "text", - geo = "uri", - title = "text", - role = "text", - logo = "uri", - org = "text", - member = "uri", - related = "uri", - categories = "text", - note = "text", - prodid = "text", - rev = "timestamp", - sound = "uri", - uid = "uri", - clientpidmap = "number, uuid", - url = "uri", - version = "text", - key = "uri", - fburl = "uri", - caladruri = "uri", - caluri = "uri", -}; - -return { - from_text = from_text; - to_text = to_text; - - from_xep54 = from_xep54; - to_xep54 = to_xep54; - - to_vcard4 = vcards_to_vcard4xml; -}; -- cgit v1.2.3 From b78c9e7f5f1ad2e696a76e01e8af70c0eda84246 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 30 May 2022 17:34:58 +0200 Subject: util.openmetrics: Set (previously unused, empty) metatable Silences luacheck warning about the metatable being unused. --- util/openmetrics.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/openmetrics.lua b/util/openmetrics.lua index cb7791ec..c18e63e9 100644 --- a/util/openmetrics.lua +++ b/util/openmetrics.lua @@ -38,7 +38,7 @@ local metric_proxy_mt = {} metric_proxy_mt.__index = metric_proxy_mt local function new_metric_proxy(metric_family, with_labels_proxy_fun) - return { + return setmetatable({ _family = metric_family, with_labels = function(self, ...) return with_labels_proxy_fun(self._family, ...) @@ -48,7 +48,7 @@ local function new_metric_proxy(metric_family, with_labels_proxy_fun) return family:with_labels(label, ...) end) end - } + }, metric_proxy_mt); end -- END of Utility: "metric proxy" -- cgit v1.2.3 From 677d77e82e0bae7d8d94c910df07ad82fdbcab6d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 30 May 2022 17:37:25 +0200 Subject: util.openmetrics: Set __name field on metatables to improve error messages Don't think we cause any such errors right now, but you never know! --- util/openmetrics.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/openmetrics.lua b/util/openmetrics.lua index c18e63e9..634f9de1 100644 --- a/util/openmetrics.lua +++ b/util/openmetrics.lua @@ -35,6 +35,7 @@ local t_pack, t_unpack = require "util.table".pack, table.unpack or unpack; --lu -- `with_partial_label` by the moduleapi in order to pre-set the `host` label -- on metrics created in non-global modules. local metric_proxy_mt = {} +metric_proxy_mt.__name = "metric_proxy" metric_proxy_mt.__index = metric_proxy_mt local function new_metric_proxy(metric_family, with_labels_proxy_fun) @@ -128,6 +129,7 @@ end -- BEGIN of generic MetricFamily implementation local metric_family_mt = {} +metric_family_mt.__name = "metric_family" metric_family_mt.__index = metric_family_mt local function histogram_metric_ctor(orig_ctor, buckets) @@ -278,6 +280,7 @@ local function compose_name(name, unit) end local metric_registry_mt = {} +metric_registry_mt.__name = "metric_registry" metric_registry_mt.__index = metric_registry_mt local function new_metric_registry(backend) -- cgit v1.2.3 From 02f67bb9171552b69088c77c3d722a53b83e8650 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 1 Jun 2022 13:59:00 +0200 Subject: util.prosodyctl.shell: Print errors in red to highlight them --- util/prosodyctl/shell.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index cad9ac00..8f910301 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -4,6 +4,8 @@ local st = require "util.stanza"; local path = require "util.paths"; local parse_args = require "util.argparse".parse; local unpack = table.unpack or _G.unpack; +local tc = require "util.termcolours"; +local isatty = require "util.pposix".isatty; local have_readline, readline = pcall(require, "readline"); @@ -64,6 +66,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); local opts, err, where = parse_args(arg); + local ttyout = isatty(io.stdout); if not opts then if err == "param-not-found" then @@ -122,7 +125,11 @@ local function start(arg) --luacheck: ignore 212/arg client.events.add_handler("received", function (stanza) if stanza.name == "repl-output" or stanza.name == "repl-result" then local result_prefix = stanza.attr.type == "error" and "!" or "|"; - print(result_prefix.." "..stanza:get_text()); + local out = result_prefix.." "..stanza:get_text(); + if ttyout and stanza.attr.type == "error" then + out = tc.getstring(tc.getstyle("red"), out); + end + print(out); end if stanza.name == "repl-result" then repl(client); -- cgit v1.2.3 From 49a9d5e4274c26272761656d3fdaa6b2509c90f5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 11 Jun 2022 21:11:01 +0100 Subject: util.watchdog: Update to use "new" util.timer API When this module was written, it wasn't possible to cancel or reschedule a timer. Times have changed, and we should take advantage of those new methods. This module becomes a very thin wrapper around util.timer now, but I'd argue it's still a very common and useful concept/abstraction to have around. Possible API change: this removes the 'last_reset' field of the watchdog. This was never really intended as a public thing, and I can't find any code that uses it, so I consider removal to be safe. --- util/watchdog.lua | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'util') diff --git a/util/watchdog.lua b/util/watchdog.lua index 516e60e4..1d30f22b 100644 --- a/util/watchdog.lua +++ b/util/watchdog.lua @@ -9,27 +9,30 @@ local watchdog_methods = {}; local watchdog_mt = { __index = watchdog_methods }; local function new(timeout, callback) - local watchdog = setmetatable({ timeout = timeout, last_reset = os_time(), callback = callback }, watchdog_mt); - timer.add_task(timeout+1, function (current_time) - local last_reset = watchdog.last_reset; - if not last_reset then - return; - end - local time_left = (last_reset + timeout) - current_time; - if time_left < 0 then - return watchdog:callback(); - end - return time_left + 1; + local watchdog = setmetatable({ + timeout = timeout; + callback = callback; + timer_id = nil; + }, watchdog_mt); + + watchdog.timer_id = timer.add_task(timeout+1, function () + return watchdog:callback(); end); + return watchdog; end function watchdog_methods:reset() - self.last_reset = os_time(); + if self.timer_id then + timer.reschedule(self.timer_id, self.timeout); + end end function watchdog_methods:cancel() - self.last_reset = nil; + if self.timer_id then + timer.stop(self.timer_id); + self.timer_id = nil; + end end return { -- cgit v1.2.3 From 029e0934783a60cf0be8c08e982440205fa9d337 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 11 Jun 2022 22:15:14 +0100 Subject: util.watchdog: Allow :reset() to restart a cancelled watchdog --- util/watchdog.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/watchdog.lua b/util/watchdog.lua index 1d30f22b..6eb2e602 100644 --- a/util/watchdog.lua +++ b/util/watchdog.lua @@ -1,6 +1,5 @@ local timer = require "util.timer"; local setmetatable = setmetatable; -local os_time = os.time; local _ENV = nil; -- luacheck: std none @@ -15,16 +14,18 @@ local function new(timeout, callback) timer_id = nil; }, watchdog_mt); - watchdog.timer_id = timer.add_task(timeout+1, function () - return watchdog:callback(); - end); + watchdog:reset(); -- Kick things off return watchdog; end function watchdog_methods:reset() if self.timer_id then - timer.reschedule(self.timer_id, self.timeout); + timer.reschedule(self.timer_id, self.timeout+1); + else + self.timer_id = timer.add_task(self.timeout+1, function () + return self:callback(); + end); end end -- cgit v1.2.3 From 7532eac0e5e4cd51a765498bdcca2d52b0b3ca7a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 11 Jun 2022 22:15:31 +0100 Subject: util.watchdog: Allow passing a new timeout to :reset() --- util/watchdog.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/watchdog.lua b/util/watchdog.lua index 6eb2e602..407028a5 100644 --- a/util/watchdog.lua +++ b/util/watchdog.lua @@ -19,7 +19,10 @@ local function new(timeout, callback) return watchdog; end -function watchdog_methods:reset() +function watchdog_methods:reset(new_timeout) + if new_timeout then + self.timeout = new_timeout; + end if self.timer_id then timer.reschedule(self.timer_id, self.timeout+1); else -- cgit v1.2.3 From ae14dc12208406f54c3ac260dc1dfe701e77f3a3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 Nov 2020 17:58:45 +0100 Subject: util.hashes: Expose sha224 and sha384 HMAC functions For completeness and consistency with set of plain hash functions --- util/hmac.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/hmac.lua b/util/hmac.lua index 4cad17cc..994ea93b 100644 --- a/util/hmac.lua +++ b/util/hmac.lua @@ -13,6 +13,8 @@ local hashes = require "util.hashes" return { md5 = hashes.hmac_md5, sha1 = hashes.hmac_sha1, + sha224 = hashes.hmac_sha224, sha256 = hashes.hmac_sha256, + sha384 = hashes.hmac_sha384, sha512 = hashes.hmac_sha512, }; -- cgit v1.2.3 From f3d61e394501472062f359e02ce5d3bb10bc4bc8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 10 Sep 2020 21:58:24 +0200 Subject: util.hashes: Bind BLAKE2 algoritms supported by OpenSSL --- util/hmac.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/hmac.lua b/util/hmac.lua index 994ea93b..ca030259 100644 --- a/util/hmac.lua +++ b/util/hmac.lua @@ -17,4 +17,6 @@ return { sha256 = hashes.hmac_sha256, sha384 = hashes.hmac_sha384, sha512 = hashes.hmac_sha512, + blake2s256 = hashes.hmac_blake2s256, + blake2b512 = hashes.hmac_blake2b512, }; -- cgit v1.2.3 From 5393bff075e8ff9edefe0752dffbc3e15a8bc773 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 Jun 2022 17:03:50 +0200 Subject: util.dependencies: Deprecate support for Lua 5.1, this is your final warning --- util/dependencies.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index d7836404..7ecbf797 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -155,7 +155,7 @@ local function log_warnings() if _VERSION > "Lua 5.4" then prosody.log("warn", "Support for %s is experimental, please report any issues", _VERSION); elseif _VERSION < "Lua 5.2" then - prosody.log("warn", "%s has several issues and support is being phased out, consider upgrading", _VERSION); + prosody.log("warn", "%s support is deprecated, upgrade as soon as possible", _VERSION); end local ssl = softreq"ssl"; if ssl then -- cgit v1.2.3 From ada68efcc9547519a636bc46fdd7a60216a578d5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Jul 2022 17:30:34 +0200 Subject: util.dependencies: Reject Lua 5.1, Lua 5.2 or later is now required (see #1600) --- util/dependencies.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index 7ecbf797..165468c5 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -32,10 +32,10 @@ local function missingdep(name, sources, msg, err) -- luacheck: ignore err end local function check_dependencies() - if _VERSION < "Lua 5.1" then + if _VERSION < "Lua 5.2" then print "***********************************" print("Unsupported Lua version: ".._VERSION); - print("At least Lua 5.1 is required."); + print("At least Lua 5.2 is required."); print "***********************************" return false; end -- cgit v1.2.3 From 49a9a1e76a34d67cfa2be24cbb2e2a9db545f969 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Jul 2022 17:31:14 +0200 Subject: util: Remove various Lua 5.1 compatibility hacks Part of #1600 --- util/bitcompat.lua | 14 -------------- util/format.lua | 3 --- util/human/io.lua | 5 +---- util/human/units.lua | 8 -------- util/prosodyctl/shell.lua | 3 +-- 5 files changed, 2 insertions(+), 31 deletions(-) (limited to 'util') diff --git a/util/bitcompat.lua b/util/bitcompat.lua index 454181af..8f227354 100644 --- a/util/bitcompat.lua +++ b/util/bitcompat.lua @@ -5,12 +5,6 @@ -- Lua 5.2 has it by default if _G.bit32 then return _G.bit32; -else - -- Lua 5.1 may have it as a standalone module that can be installed - local ok, bitop = pcall(require, "bit32") - if ok then - return bitop; - end end do @@ -21,12 +15,4 @@ do end end -do - -- Lastly, try the LuaJIT bitop library - local ok, bitop = pcall(require, "bit") - if ok then - return bitop; - end -end - error "No bit module found. See https://prosody.im/doc/depends#bitop"; diff --git a/util/format.lua b/util/format.lua index d709aada..611068d7 100644 --- a/util/format.lua +++ b/util/format.lua @@ -35,7 +35,6 @@ local control_symbols = { ["\030"] = "\226\144\158", ["\031"] = "\226\144\159", ["\127"] = "\226\144\161", }; local supports_p = pcall(string.format, "%p", ""); -- >= Lua 5.4 -local supports_a = pcall(string.format, "%a", 0.0); -- > Lua 5.1 local function format(formatstring, ...) local args = pack(...); @@ -93,8 +92,6 @@ local function format(formatstring, ...) elseif expects_positive[option] and arg < 0 then args[i] = tostring(arg); return "[%s]"; - elseif (option == "a" or option == "A") and not supports_a then - return "%x"; else return -- acceptable number end diff --git a/util/human/io.lua b/util/human/io.lua index 7d7dea97..4fce0e94 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -30,10 +30,7 @@ local function getline() end local function getpass() - local stty_ret, _, status_code = os.execute("stty -echo 2>/dev/null"); - if status_code then -- COMPAT w/ Lua 5.1 - stty_ret = status_code; - end + local stty_ret = os.execute("stty -echo 2>/dev/null"); if stty_ret ~= 0 then io.write("\027[08m"); -- ANSI 'hidden' text attribute end diff --git a/util/human/units.lua b/util/human/units.lua index af233e98..23e3e579 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -6,14 +6,6 @@ local math_max = math.max; local math_min = math.min; local unpack = table.unpack or unpack; --luacheck: ignore 113 -if math_log(10, 10) ~= 1 then - -- Lua 5.1 COMPAT - local log10 = math.log10; - function math_log(n, base) - return log10(n) / log10(base); - end -end - local large = { "k", 1000, "M", 1000000, diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 8f910301..5f99bec1 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -80,8 +80,7 @@ local function start(arg) --luacheck: ignore 212/arg if arg[1] then if arg[2] then -- prosodyctl shell module reload foo bar.com --> module:reload("foo", "bar.com") - -- COMPAT Lua 5.1 doesn't have the separator argument to string.rep - arg[1] = string.format("%s:%s("..string.rep("%q, ", #arg-2):sub(1, -3)..")", unpack(arg)); + arg[1] = string.format("%s:%s("..string.rep("%q", #arg-2,", ")..")", unpack(arg)); end client.events.add_handler("connected", function() -- cgit v1.2.3 From bcc6efb2aa55b5f028a50773ffff50b60622ab71 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 5 Jul 2022 14:18:32 +0200 Subject: util.envload: Remove Lua 5.1 method Part of #1600 Is this module even needed anymore? --- util/envload.lua | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) (limited to 'util') diff --git a/util/envload.lua b/util/envload.lua index 6182a1f9..cf45b702 100644 --- a/util/envload.lua +++ b/util/envload.lua @@ -6,38 +6,19 @@ -- -- luacheck: ignore 113/setfenv 113/loadstring -local load, loadstring, setfenv = load, loadstring, setfenv; +local load = load; local io_open = io.open; -local envload; -local envloadfile; -if setfenv then - function envload(code, source, env) - local f, err = loadstring(code, source); - if f and env then setfenv(f, env); end - return f, err; - end - - function envloadfile(file, env) - local fh, err, errno = io_open(file); - if not fh then return fh, err, errno; end - local f, err = load(function () return fh:read(2048); end, "@"..file); - fh:close(); - if f and env then setfenv(f, env); end - return f, err; - end -else - function envload(code, source, env) - return load(code, source, nil, env); - end +local function envload(code, source, env) + return load(code, source, nil, env); +end - function envloadfile(file, env) - local fh, err, errno = io_open(file); - if not fh then return fh, err, errno; end - local f, err = load(fh:lines(2048), "@"..file, nil, env); - fh:close(); - return f, err; - end +local function envloadfile(file, env) + local fh, err, errno = io_open(file); + if not fh then return fh, err, errno; end + local f, err = load(fh:lines(2048), "@" .. file, nil, env); + fh:close(); + return f, err; end return { envload = envload, envloadfile = envloadfile }; -- cgit v1.2.3 From 5251c9b686fc7885c1213cc2580d66ebda2dda9b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 Jul 2022 19:07:38 +0200 Subject: compat: Remove handling of Lua 5.1 location of 'unpack' function --- util/format.lua | 2 +- util/human/units.lua | 2 +- util/import.lua | 2 +- util/iterators.lua | 2 +- util/multitable.lua | 2 +- util/openmetrics.lua | 2 +- util/promise.lua | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 611068d7..35694271 100644 --- a/util/format.lua +++ b/util/format.lua @@ -6,7 +6,7 @@ -- Provides some protection from e.g. CAPEC-135, CWE-117, CWE-134, CWE-93 local tostring = tostring; -local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack +local unpack = table.unpack; local pack = require "util.table".pack; -- TODO table.pack in 5.2+ local valid_utf8 = require "util.encodings".utf8.valid; local type = type; diff --git a/util/human/units.lua b/util/human/units.lua index 23e3e579..329c8518 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -4,7 +4,7 @@ local math_floor = math.floor; local math_log = math.log; local math_max = math.max; local math_min = math.min; -local unpack = table.unpack or unpack; --luacheck: ignore 113 +local unpack = table.unpack; local large = { "k", 1000, diff --git a/util/import.lua b/util/import.lua index 1007bc0a..0892e9b1 100644 --- a/util/import.lua +++ b/util/import.lua @@ -8,7 +8,7 @@ -local unpack = table.unpack or unpack; --luacheck: ignore 113 +local unpack = table.unpack; local t_insert = table.insert; function _G.import(module, ...) local m = package.loaded[module] or require(module); diff --git a/util/iterators.lua b/util/iterators.lua index c03c2fd6..e9f7c1b2 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -12,7 +12,7 @@ local it = {}; local t_insert = table.insert; local next = next; -local unpack = table.unpack or unpack; --luacheck: ignore 113 +local unpack = table.unpack; local pack = table.pack or require "util.table".pack; local type = type; local table, setmetatable = table, setmetatable; diff --git a/util/multitable.lua b/util/multitable.lua index 4f2cd972..0c292b45 100644 --- a/util/multitable.lua +++ b/util/multitable.lua @@ -9,7 +9,7 @@ local select = select; local t_insert = table.insert; local pairs, next, type = pairs, next, type; -local unpack = table.unpack or unpack; --luacheck: ignore 113 +local unpack = table.unpack; local _ENV = nil; -- luacheck: std none diff --git a/util/openmetrics.lua b/util/openmetrics.lua index 634f9de1..f451e504 100644 --- a/util/openmetrics.lua +++ b/util/openmetrics.lua @@ -26,7 +26,7 @@ local log = require "util.logger".init("util.openmetrics"); local new_multitable = require "util.multitable".new; local iter_multitable = require "util.multitable".iter; local t_concat, t_insert = table.concat, table.insert; -local t_pack, t_unpack = require "util.table".pack, table.unpack or unpack; --luacheck: ignore 113/unpack +local t_pack, t_unpack = require "util.table".pack, table.unpack; -- BEGIN of Utility: "metric proxy" -- This allows to wrap a MetricFamily in a proxy which only provides the diff --git a/util/promise.lua b/util/promise.lua index c4e166ed..1762d501 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -2,7 +2,7 @@ local promise_methods = {}; local promise_mt = { __name = "promise", __index = promise_methods }; local xpcall = require "util.xpcall".xpcall; -local unpack = table.unpack or unpack; --luacheck: ignore 113 +local unpack = table.unpack; function promise_mt:__tostring() return "promise (" .. (self._state or "invalid") .. ")"; -- cgit v1.2.3 From f8e73eba98a73e5d3dd14f73d7ce66e5503efbb4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 Jul 2022 19:15:24 +0200 Subject: compat: Use table.pack (there since Lua 5.2) over our util.table Added in d278a770eddc avoid having to deal with its absence in Lua 5.1. No longer needed when Lua 5.1 support is dropped. --- util/format.lua | 2 +- util/iterators.lua | 2 +- util/openmetrics.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 35694271..203bdeab 100644 --- a/util/format.lua +++ b/util/format.lua @@ -7,7 +7,7 @@ local tostring = tostring; local unpack = table.unpack; -local pack = require "util.table".pack; -- TODO table.pack in 5.2+ +local pack = table.pack; local valid_utf8 = require "util.encodings".utf8.valid; local type = type; local dump = require "util.serialization".new("debug"); diff --git a/util/iterators.lua b/util/iterators.lua index e9f7c1b2..4529697a 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -13,7 +13,7 @@ local it = {}; local t_insert = table.insert; local next = next; local unpack = table.unpack; -local pack = table.pack or require "util.table".pack; +local pack = table.pack; local type = type; local table, setmetatable = table, setmetatable; diff --git a/util/openmetrics.lua b/util/openmetrics.lua index f451e504..8c77ffcd 100644 --- a/util/openmetrics.lua +++ b/util/openmetrics.lua @@ -26,7 +26,7 @@ local log = require "util.logger".init("util.openmetrics"); local new_multitable = require "util.multitable".new; local iter_multitable = require "util.multitable".iter; local t_concat, t_insert = table.concat, table.insert; -local t_pack, t_unpack = require "util.table".pack, table.unpack; +local t_pack, t_unpack = table.pack, table.unpack; -- BEGIN of Utility: "metric proxy" -- This allows to wrap a MetricFamily in a proxy which only provides the -- cgit v1.2.3 From c1a166daa79ec4fadd2083dd7097216068f74a28 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 27 Jul 2022 00:32:04 +0200 Subject: util.sasl.scram: Add 'tls-exporter' as recognised channel binding method The last missing piece of #1760, otherwise SCRAM-SHA-*-PLUS is not actually advertised. --- util/sasl/scram.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 37abf4a4..4606d1fd 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -240,7 +240,7 @@ local function init(registerMechanism) -- register channel binding equivalent registerMechanism("SCRAM-"..hash_name.."-PLUS", {"plain", "scram_"..(hashprep(hash_name))}, - scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db, true), {"tls-unique"}); + scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db, true), {"tls-unique", "tls-exporter"}); end registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); -- cgit v1.2.3 From 93830ecf40f487100170e22782d1966aa325067c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 5 Aug 2022 16:54:15 +0200 Subject: various: Update IETF RFC URLs for tools.ietf.org transition See https://www.ietf.org/blog/finalizing-ietf-tools-transition/ Already done in various other places. --- util/x509.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index 76b50076..51ca3c96 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -11,12 +11,12 @@ -- IDN libraries complicate that. --- [TLS-CERTS] - http://tools.ietf.org/html/rfc6125 --- [XMPP-CORE] - http://tools.ietf.org/html/rfc6120 --- [SRV-ID] - http://tools.ietf.org/html/rfc4985 --- [IDNA] - http://tools.ietf.org/html/rfc5890 --- [LDAP] - http://tools.ietf.org/html/rfc4519 --- [PKIX] - http://tools.ietf.org/html/rfc5280 +-- [TLS-CERTS] - https://www.rfc-editor.org/rfc/rfc6125.html +-- [XMPP-CORE] - https://www.rfc-editor.org/rfc/rfc6120.html +-- [SRV-ID] - https://www.rfc-editor.org/rfc/rfc4985.html +-- [IDNA] - https://www.rfc-editor.org/rfc/rfc5890.html +-- [LDAP] - https://www.rfc-editor.org/rfc/rfc4519.html +-- [PKIX] - https://www.rfc-editor.org/rfc/rfc5280.html local nameprep = require "util.encodings".stringprep.nameprep; local idna_to_ascii = require "util.encodings".idna.to_ascii; -- cgit v1.2.3 From 6619b204e37f7d0b7c18a1194688f3c85e8447fa Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Aug 2022 20:33:44 +0200 Subject: doap: Update XEP versions for which no code changes appear needed XEP-0004: Partial forms are handled XEP-0045: We're already strict with GC 1.0 XEP-0060: Change in semantics wrt 'pubsub#type', but not in code XEP-0115: No protocol change XEP-0138: Specification moved to Obsolete XEP-0163: Editorial only change XEP-0215: Minor schema change XEP-0280: Editorial change XEP-0297: Had the wrong version number XEP-0106: Note missing piece for version 1.1 XEP-0313: Editorial change XEP-0363: Editorial clarification, no code change required XEP-0380: Registry additions, no code change needed XEP-0384: Not directly supported, only here because people will ask otherwise XEP-0445: Broken out of XEP-0401 --- util/jid.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/jid.lua b/util/jid.lua index 694a6b1f..759af746 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -111,6 +111,7 @@ local function resource(jid) return (select(3, split(jid))); end +-- TODO Forbid \20 at start and end of escaped output per XEP-0106 v1.1 local function escape(s) return s and (s:gsub("\\%x%x", backslash_escapes):gsub("[\"&'/:<>@ ]", escapes)); end local function unescape(s) return s and (s:gsub("\\%x%x", unescapes)); end -- cgit v1.2.3 From c85c18b03ab627ee81273d4c0f0009e1a5cc2d03 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 14 Aug 2022 16:57:31 +0200 Subject: util.datetime: Add support for sub-second precision timestamps Lua since 5.3 raises a fuss when time functions are handed a number with a fractional part and the underlying C functions are all based on integer seconds without support for more precision. --- util/datetime.lua | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/datetime.lua b/util/datetime.lua index 2d27ece4..8fa1a778 100644 --- a/util/datetime.lua +++ b/util/datetime.lua @@ -12,31 +12,42 @@ local os_date = os.date; local os_time = os.time; local os_difftime = os.difftime; +local floor = math.floor; local tonumber = tonumber; local _ENV = nil; -- luacheck: std none local function date(t) - return os_date("!%Y-%m-%d", t); + return os_date("!%Y-%m-%d", t and floor(t) or nil); end local function datetime(t) - return os_date("!%Y-%m-%dT%H:%M:%SZ", t); + if t == nil or t % 1 == 0 then + return os_date("!%Y-%m-%dT%H:%M:%SZ", t); + end + local m = t % 1; + local s = floor(t); + return os_date("!%Y-%m-%dT%H:%M:%S.%%06dZ", s):format(floor(m * 1000000)); end local function time(t) - return os_date("!%H:%M:%S", t); + if t == nil or t % 1 == 0 then + return os_date("!%H:%M:%S", t); + end + local m = t % 1; + local s = floor(t); + return os_date("!%H:%M:%S.%%06d", s):format(floor(m * 1000000)); end local function legacy(t) - return os_date("!%Y%m%dT%H:%M:%S", t); + return os_date("!%Y%m%dT%H:%M:%S", t and floor(t) or nil); end local function parse(s) if s then local year, month, day, hour, min, sec, tzd; - year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-]?.*)$"); + year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$"); if year then local now = os_time(); local time_offset = os_difftime(os_time(os_date("*t", now)), os_time(os_date("!*t", now))); -- to deal with local timezone @@ -49,8 +60,9 @@ local function parse(s) tzd_offset = h * 60 * 60 + m * 60; if sign == "-" then tzd_offset = -tzd_offset; end end - sec = (sec + time_offset) - tzd_offset; - return os_time({year=year, month=month, day=day, hour=hour, min=min, sec=sec, isdst=false}); + local prec = sec%1; + sec = floor(sec + time_offset) - tzd_offset; + return os_time({year=year, month=month, day=day, hour=hour, min=min, sec=sec, isdst=false})+prec; end end end -- cgit v1.2.3 From 68867c09d6c6298fcbea882b884005dbb8aa2792 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Aug 2022 18:07:31 +0200 Subject: util.datetime: Remove a line No idea why the locals were declared on a line by itself. Perhaps line length considerations? But saving 6 characters in width by adding a whole line with 47 characters seems excessive. This is still within the 150 character limit set by .luacheckrc --- util/datetime.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/datetime.lua b/util/datetime.lua index 8fa1a778..6df146f4 100644 --- a/util/datetime.lua +++ b/util/datetime.lua @@ -46,8 +46,7 @@ end local function parse(s) if s then - local year, month, day, hour, min, sec, tzd; - year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$"); + local year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$"); if year then local now = os_time(); local time_offset = os_difftime(os_time(os_date("*t", now)), os_time(os_date("!*t", now))); -- to deal with local timezone -- cgit v1.2.3 From 8959868a28fb74e55ce41a9cb93d7e3524c7dc2b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Aug 2022 19:04:30 +0200 Subject: util.stanza: Add method for extracting a single attribute value Sometimes you only care about a single attribute, but the child tag itself may be optional, leading to needing `tag and tag.attr.foo` or `stanza:find("tag@foo")`. The `:find()` method is fairly complex, so avoiding it for this kind of simpler use case is a win. --- util/stanza.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index a14be5f0..b75a1f32 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -176,6 +176,14 @@ function stanza_mt:get_child_text(name, xmlns) return nil; end +function stanza_mt:get_child_attr(name, xmlns, attr) + local tag = self:get_child(name, xmlns); + if tag then + return tag.attr[attr]; + end + return nil; +end + function stanza_mt:child_with_name(name) for _, child in ipairs(self.tags) do if child.name == name then return child; end -- cgit v1.2.3 From 7ebea134e6fd45a524b8a924be04c3b51f8f7799 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 15 Jun 2022 11:37:09 +0100 Subject: util.session: Add role management methods --- util/session.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/session.lua b/util/session.lua index 25b22faf..d908476a 100644 --- a/util/session.lua +++ b/util/session.lua @@ -57,10 +57,16 @@ local function set_send(session) return session; end +local function set_role(session, role) + session.role = role; +end + return { new = new_session; + set_id = set_id; set_logger = set_logger; set_conn = set_conn; set_send = set_send; + set_role = set_role; } -- cgit v1.2.3 From 77146aa4e7841eb5ca35cca28881d37eb53e5a96 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 19 Jul 2022 17:44:26 +0100 Subject: util.roles: Add new utility module to consolidate role objects and methods --- util/roles.lua | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 util/roles.lua (limited to 'util') diff --git a/util/roles.lua b/util/roles.lua new file mode 100644 index 00000000..831ce6ae --- /dev/null +++ b/util/roles.lua @@ -0,0 +1,100 @@ +local array = require "util.array"; +local it = require "util.iterators"; +local new_short_id = require "util.id".short; + +local role_methods = {}; +local role_mt = { + __index = role_methods; + __name = "role"; + __add = nil; +}; + +local function is_role(o) + local mt = getmetatable(o); + return mt == role_mt; +end + +local function _new_may(permissions, inherited_mays) + local n_inherited = inherited_mays and #inherited_mays; + return function (role, action, context) + -- Note: 'role' may be a descendent role, not only the one we're attached to + local policy = permissions[action]; + if policy ~= nil then + return policy; + end + if n_inherited then + for i = 1, n_inherited do + policy = inherited_mays[i](role, action, context); + if policy ~= nil then + return policy; + end + end + end + return false; + end +end + +local permissions_key = {}; + +-- { +-- Required: +-- name = "My fancy role"; +-- +-- Optional: +-- inherits = { role_obj... } +-- default = true +-- priority = 100 +-- permissions = { +-- ["foo"] = true; -- allow +-- ["bar"] = false; -- deny +-- } +-- } +local function new(base_config, overrides) + local config = setmetatable(overrides or {}, { __index = base_config }); + local permissions = {}; + local inherited_mays; + if config.inherits then + inherited_mays = array.pluck(config.inherits, "may"); + end + local new_role = { + id = new_short_id(); + name = config.name; + description = config.description; + default = config.default; + priority = config.priority; + may = _new_may(permissions, inherited_mays); + inherits = config.inherits; + [permissions_key] = permissions; + }; + local desired_permissions = config.permissions or config[permissions_key]; + for k, v in pairs(desired_permissions or {}) do + permissions[k] = v; + end + return setmetatable(new_role, role_mt); +end + +function role_methods:clone(overrides) + return new(self, overrides); +end + +function role_methods:set_permission(permission_name, policy, overwrite) + local permissions = self[permissions_key]; + if overwrite ~= true and permissions[permission_name] ~= nil and permissions[permission_name] ~= policy then + return false, "policy-already-exists"; + end + permissions[permission_name] = policy; + return true; +end + +function role_mt.__tostring(self) + return ("role<[%s] %s>"):format(self.id or "nil", self.name or "[no name]"); +end + +function role_mt.__pairs(self) + return it.filter(permissions_key, next, self); +end + +return { + is_role = is_role; + new = new; +}; -- cgit v1.2.3 From b02ab508d51481e19991053645ed48dcb819c4d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 29 Aug 2022 14:59:46 +0100 Subject: util.stanza: Add add_error() to simplify adding error tags to existing stanzas Some fiddling is required now in error_reply() to ensure the cursor is in the same place as before this change (a lot of code apparently uses that feature). --- util/stanza.lua | 56 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index b75a1f32..24f3cb33 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -29,6 +29,7 @@ local valid_utf8 = require "util.encodings".utf8.valid; local do_pretty_printing, termcolours = pcall(require, "util.termcolours"); local xmlns_stanzas = "urn:ietf:params:xml:ns:xmpp-stanzas"; +local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; local _ENV = nil; -- luacheck: std none @@ -396,6 +397,33 @@ function stanza_mt.get_error(stanza) return error_type, condition or "undefined-condition", text, extra_tag; end +function stanza_mt.add_error(stanza, error_type, condition, error_message, error_by) + local extra; + if type(error_type) == "table" then -- an util.error or similar object + if type(error_type.extra) == "table" then + extra = error_type.extra; + end + if type(error_type.context) == "table" and type(error_type.context.by) == "string" then error_by = error_type.context.by; end + error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; + end + if stanza.attr.from == error_by then + error_by = nil; + end + stanza:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here + :tag(condition, xmpp_stanzas_attr); + if extra and condition == "gone" and type(extra.uri) == "string" then + stanza:text(extra.uri); + end + stanza:up(); + if error_message then stanza:text_tag("text", error_message, xmpp_stanzas_attr); end + if extra and is_stanza(extra.tag) then + stanza:add_child(extra.tag); + elseif extra and extra.namespace and extra.condition then + stanza:tag(extra.condition, { xmlns = extra.namespace }):up(); + end + return stanza:up(); +end + local function preserialize(stanza) local s = { name = stanza.name, attr = stanza.attr }; for _, child in ipairs(stanza) do @@ -470,7 +498,6 @@ local function reply(orig) }); end -local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; local function error_reply(orig, error_type, condition, error_message, error_by) if not is_stanza(orig) then error("bad argument to error_reply: expected stanza, got "..type(orig)); @@ -479,30 +506,9 @@ local function error_reply(orig, error_type, condition, error_message, error_by) end local t = reply(orig); t.attr.type = "error"; - local extra; - if type(error_type) == "table" then -- an util.error or similar object - if type(error_type.extra) == "table" then - extra = error_type.extra; - end - if type(error_type.context) == "table" and type(error_type.context.by) == "string" then error_by = error_type.context.by; end - error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; - end - if t.attr.from == error_by then - error_by = nil; - end - t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here - :tag(condition, xmpp_stanzas_attr); - if extra and condition == "gone" and type(extra.uri) == "string" then - t:text(extra.uri); - end - t:up(); - if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end - if extra and is_stanza(extra.tag) then - t:add_child(extra.tag); - elseif extra and extra.namespace and extra.condition then - t:tag(extra.condition, { xmlns = extra.namespace }):up(); - end - return t; -- stanza ready for adding app-specific errors + t:add_error(error_type, condition, error_message, error_by); + t.last_add = { t[1] }; -- ready to add application-specific errors + return t; end local function presence(attr) -- cgit v1.2.3 From 4122acf8c8e80d7654e9ba7e1297a38e834f49de Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 24 Jun 2022 17:03:28 +0100 Subject: util.paseto: Implementation of PASETO v4.public tokens PASETO provides an alternative to JWT with the promise of fewer implementation pitfalls. The v4.public algorithm allows asymmetric cryptographically-verified token issuance and validation. In summary, such tokens can be issued by one party and securely verified by any other party independently using the public key of the issuer. This has a number of potential applications in a decentralized network and ecosystem such as XMPP. For example, such tokens could be combined with XEP-0317 to allow hats to be verified even in the context of a third-party MUC service. --- util/paseto.lua | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 util/paseto.lua (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua new file mode 100644 index 00000000..5f162ad0 --- /dev/null +++ b/util/paseto.lua @@ -0,0 +1,123 @@ +local crypto = require "util.crypto"; +local json = require "util.json"; +local base64_encode = require "util.encodings".base64.encode; +local base64_decode = require "util.encodings".base64.decode; +local secure_equals = require "util.hashes".equals; +local bit = require "util.bitcompat"; +local s_pack = require "util.struct".pack; + +local s_gsub = string.gsub; + +local pubkey_methods = {}; +local privkey_methods = {}; + +local v4_public_pubkey_mt = { __index = pubkey_methods }; +local v4_public_privkey_mt = { __index = privkey_methods }; +local v4_public = {}; + +local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" }; +local function b64url(data) + return (s_gsub(base64_encode(data), "[+/=]", b64url_rep)); +end +local function unb64url(data) + return base64_decode(s_gsub(data, "[-_]", b64url_rep).."=="); +end + +local function le64(n) + return s_pack(" Date: Fri, 1 Jul 2022 18:51:15 +0100 Subject: util.jwt: Add support/tests for ES256 via improved API and using util.crypto In many cases code will be either signing or verifying. With asymmetric algorithms it's clearer and more efficient to just state that once, instead of passing keys (and possibly other parameters) with every sign/verify call. This also allows earlier validation of the key used. The previous (HS256-only) sign/verify methods continue to be exposed for backwards-compatibility. --- util/jwt.lua | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 19 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index bf106dfa..58888b5d 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -1,4 +1,5 @@ local s_gsub = string.gsub; +local crypto = require "util.crypto"; local json = require "util.json"; local hashes = require "util.hashes"; local base64_encode = require "util.encodings".base64.encode; @@ -13,17 +14,8 @@ local function unb64url(data) return base64_decode(s_gsub(data, "[-_]", b64url_rep).."=="); end -local static_header = b64url('{"alg":"HS256","typ":"JWT"}') .. '.'; - -local function sign(key, payload) - local encoded_payload = json.encode(payload); - local signed = static_header .. b64url(encoded_payload); - local signature = hashes.hmac_sha256(key, signed); - return signed .. "." .. b64url(signature); -end - local jwt_pattern = "^(([A-Za-z0-9-_]+)%.([A-Za-z0-9-_]+))%.([A-Za-z0-9-_]+)$" -local function verify(key, blob) +local function decode_jwt(blob, expected_alg) local signed, bheader, bpayload, signature = string.match(blob, jwt_pattern); if not signed then return nil, "invalid-encoding"; @@ -31,21 +23,131 @@ local function verify(key, blob) local header = json.decode(unb64url(bheader)); if not header or type(header) ~= "table" then return nil, "invalid-header"; - elseif header.alg ~= "HS256" then + elseif header.alg ~= expected_alg then return nil, "unsupported-algorithm"; end - if not secure_equals(b64url(hashes.hmac_sha256(key, signed)), signature) then - return false, "signature-mismatch"; + return signed, signature, bpayload; +end + +local function new_static_header(algorithm_name) + return b64url('{"alg":"'..algorithm_name..'","typ":"JWT"}') .. '.'; +end + +-- HS*** family +local function new_hmac_algorithm(name, hmac) + local static_header = new_static_header(name); + + local function sign(key, payload) + local encoded_payload = json.encode(payload); + local signed = static_header .. b64url(encoded_payload); + local signature = hmac(key, signed); + return signed .. "." .. b64url(signature); + end + + local function verify(key, blob) + local signed, signature, raw_payload = decode_jwt(blob, name); + if not signed then return nil, signature; end -- nil, err + + if not secure_equals(b64url(hmac(key, signed)), signature) then + return false, "signature-mismatch"; + end + local payload, err = json.decode(unb64url(raw_payload)); + if err ~= nil then + return nil, "json-decode-error"; + end + return true, payload; end - local payload, err = json.decode(unb64url(bpayload)); - if err ~= nil then - return nil, "json-decode-error"; + + local function load_key(key) + assert(type(key) == "string", "key must be string (long, random, secure)"); + return key; + end + + return { sign = sign, verify = verify, load_key = load_key }; +end + +-- ES*** family +local function new_ecdsa_algorithm(name, c_sign, c_verify) + local static_header = new_static_header(name); + + return { + sign = function (private_key, payload) + local encoded_payload = json.encode(payload); + local signed = static_header .. b64url(encoded_payload); + + local der_sig = c_sign(private_key, signed); + + local r, s = crypto.parse_ecdsa_signature(der_sig); + + return signed.."."..b64url(r..s); + end; + + verify = function (public_key, blob) + local signed, signature, raw_payload = decode_jwt(blob, name); + if not signed then return nil, signature; end -- nil, err + + local raw_signature = unb64url(signature); + + local der_sig = crypto.build_ecdsa_signature(raw_signature:sub(1, 32), raw_signature:sub(33, 64)); + if not der_sig then + return false, "signature-mismatch"; + end + + local verify_ok = c_verify(public_key, signed, der_sig); + if not verify_ok then + return false, "signature-mismatch"; + end + + local payload, err = json.decode(unb64url(raw_payload)); + if err ~= nil then + return nil, "json-decode-error"; + end + + return true, payload; + end; + + load_public_key = function (public_key_pem) + local key = assert(crypto.import_public_pem(public_key_pem)); + assert(key:get_type() == "id-ecPublicKey", "incorrect key type"); + return key; + end; + + load_private_key = function (private_key_pem) + local key = assert(crypto.import_private_pem(private_key_pem)); + assert(key:get_type() == "id-ecPublicKey", "incorrect key type"); + return key; + end; + }; +end + +local algorithms = { + HS256 = new_hmac_algorithm("HS256", hashes.hmac_sha256); + ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify); +}; + +local function new_signer(algorithm, key_input) + local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm); + local key = (impl.load_private_key or impl.load_key)(key_input); + local sign = impl.sign; + return function (payload) + return sign(key, payload); + end +end + +local function new_verifier(algorithm, key_input) + local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm); + local key = (impl.load_public_key or impl.load_key)(key_input); + local verify = impl.verify; + return function (token) + return verify(key, token); end - return true, payload; end return { - sign = sign; - verify = verify; + new_signer = new_signer; + new_verifier = new_verifier; + -- Deprecated + sign = algorithms.HS256.sign; + verify = algorithms.HS256.verify; }; -- cgit v1.2.3 From 0b0555c3390287d2416485b1549186a282c6d1b1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 2 Jul 2022 12:26:43 +0100 Subject: util.jwt: Add support for RSA-based algorithms (RS256, PS256) --- util/jwt.lua | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 58888b5d..433a69f6 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -66,8 +66,7 @@ local function new_hmac_algorithm(name, hmac) return { sign = sign, verify = verify, load_key = load_key }; end --- ES*** family -local function new_ecdsa_algorithm(name, c_sign, c_verify) +local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode, sig_decode) local static_header = new_static_header(name); return { @@ -75,25 +74,27 @@ local function new_ecdsa_algorithm(name, c_sign, c_verify) local encoded_payload = json.encode(payload); local signed = static_header .. b64url(encoded_payload); - local der_sig = c_sign(private_key, signed); - - local r, s = crypto.parse_ecdsa_signature(der_sig); + local signature = c_sign(private_key, signed); + if sig_encode then + signature = sig_encode(signature); + end - return signed.."."..b64url(r..s); + return signed.."."..b64url(signature); end; - verify = function (public_key, blob) + verify = function (public_key, blob) local signed, signature, raw_payload = decode_jwt(blob, name); if not signed then return nil, signature; end -- nil, err - local raw_signature = unb64url(signature); - - local der_sig = crypto.build_ecdsa_signature(raw_signature:sub(1, 32), raw_signature:sub(33, 64)); - if not der_sig then + signature = unb64url(signature); + if sig_decode and signature then + signature = sig_decode(signature); + end + if not signature then return false, "signature-mismatch"; end - local verify_ok = c_verify(public_key, signed, der_sig); + local verify_ok = c_verify(public_key, signed, signature); if not verify_ok then return false, "signature-mismatch"; end @@ -108,21 +109,41 @@ local function new_ecdsa_algorithm(name, c_sign, c_verify) load_public_key = function (public_key_pem) local key = assert(crypto.import_public_pem(public_key_pem)); - assert(key:get_type() == "id-ecPublicKey", "incorrect key type"); + assert(key:get_type() == key_type, "incorrect key type"); return key; end; load_private_key = function (private_key_pem) local key = assert(crypto.import_private_pem(private_key_pem)); - assert(key:get_type() == "id-ecPublicKey", "incorrect key type"); + assert(key:get_type() == key_type, "incorrect key type"); return key; end; }; end +-- RS***, PS*** +local function new_rsa_algorithm(name, c_sign, c_verify) + return new_crypto_algorithm(name, "rsaEncryption", c_sign, c_verify); +end + +-- ES*** +local function new_ecdsa_algorithm(name, c_sign, c_verify) + local function encode_ecdsa_sig(der_sig) + local r, s = crypto.parse_ecdsa_signature(der_sig); + return r..s; + end + + local function decode_ecdsa_sig(jwk_sig) + return crypto.build_ecdsa_signature(jwk_sig:sub(1, 32), jwk_sig:sub(33, 64)); + end + return new_crypto_algorithm(name, "id-ecPublicKey", c_sign, c_verify, encode_ecdsa_sig, decode_ecdsa_sig); +end + local algorithms = { HS256 = new_hmac_algorithm("HS256", hashes.hmac_sha256); ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify); + RS256 = new_rsa_algorithm("RS256", crypto.rsassa_pkcs1_256_sign, crypto.rsassa_pkcs1_256_verify); + PS256 = new_rsa_algorithm("PS256", crypto.rsassa_pss_256_sign, crypto.rsassa_pss_256_verify); }; local function new_signer(algorithm, key_input) -- cgit v1.2.3 From 5316b0005e042f1abeb1ac5fed8715ab65e6752c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 2 Jul 2022 14:59:52 +0100 Subject: util.crypto: More digests for sign/verify, use macros for clarity/consistency --- util/jwt.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 433a69f6..7a05e45d 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -142,8 +142,8 @@ end local algorithms = { HS256 = new_hmac_algorithm("HS256", hashes.hmac_sha256); ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify); - RS256 = new_rsa_algorithm("RS256", crypto.rsassa_pkcs1_256_sign, crypto.rsassa_pkcs1_256_verify); - PS256 = new_rsa_algorithm("PS256", crypto.rsassa_pss_256_sign, crypto.rsassa_pss_256_verify); + RS256 = new_rsa_algorithm("RS256", crypto.rsassa_pkcs1_sha256_sign, crypto.rsassa_pkcs1_sha256_verify); + PS256 = new_rsa_algorithm("PS256", crypto.rsassa_pss_sha256_sign, crypto.rsassa_pss_sha256_verify); }; local function new_signer(algorithm, key_input) -- cgit v1.2.3 From fad58c5ab2fa449aea07eca2a99a1b5e30a22711 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 2 Jul 2022 15:29:04 +0100 Subject: util.jwt: All the algorithms (+ all the tests!) Except 'none'. Not implementing that one. --- util/jwt.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 7a05e45d..7bd98eb1 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -34,9 +34,11 @@ local function new_static_header(algorithm_name) end -- HS*** family -local function new_hmac_algorithm(name, hmac) +local function new_hmac_algorithm(name) local static_header = new_static_header(name); + local hmac = hashes["hmac_sha"..name:sub(-3)]; + local function sign(key, payload) local encoded_payload = json.encode(payload); local signed = static_header .. b64url(encoded_payload); @@ -122,7 +124,11 @@ local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode end -- RS***, PS*** -local function new_rsa_algorithm(name, c_sign, c_verify) +local rsa_sign_algos = { RS = "rsassa_pkcs1", PS = "rsassa_pss" }; +local function new_rsa_algorithm(name) + local family, digest_bits = name:match("^(..)(...)$"); + local c_sign = crypto[rsa_sign_algos[family].."_sha"..digest_bits.."_sign"]; + local c_verify = crypto[rsa_sign_algos[family].."_sha"..digest_bits.."_verify"]; return new_crypto_algorithm(name, "rsaEncryption", c_sign, c_verify); end @@ -140,10 +146,10 @@ local function new_ecdsa_algorithm(name, c_sign, c_verify) end local algorithms = { - HS256 = new_hmac_algorithm("HS256", hashes.hmac_sha256); + HS256 = new_hmac_algorithm("HS256"), HS384 = new_hmac_algorithm("HS384"), HS512 = new_hmac_algorithm("HS512"); ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify); - RS256 = new_rsa_algorithm("RS256", crypto.rsassa_pkcs1_sha256_sign, crypto.rsassa_pkcs1_sha256_verify); - PS256 = new_rsa_algorithm("PS256", crypto.rsassa_pss_sha256_sign, crypto.rsassa_pss_sha256_verify); + RS256 = new_rsa_algorithm("RS256"), RS384 = new_rsa_algorithm("RS384"), RS512 = new_rsa_algorithm("RS512"); + PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512"); }; local function new_signer(algorithm, key_input) @@ -167,6 +173,7 @@ end return { new_signer = new_signer; new_verifier = new_verifier; + _algorithms = algorithms; -- Deprecated sign = algorithms.HS256.sign; verify = algorithms.HS256.verify; -- cgit v1.2.3 From 23458111dcf59d6c64e77f3286dbaa752539b77a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 13:28:29 +0100 Subject: util.jwt: Provide built-in token expiry support (defaults to 3600s lifetime) To avoid every user of the library needing to add and verify expiry info, this is now handled by util.jwt itself (if not overridden or disabled). Issuing tokens that are valid forever is bad practice and rarely desired, and the default token lifetime is now 3600s (1 hour). --- util/jwt.lua | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 7bd98eb1..3501d9f2 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -152,21 +152,46 @@ local algorithms = { PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512"); }; -local function new_signer(algorithm, key_input) +local function new_signer(algorithm, key_input, options) local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm); local key = (impl.load_private_key or impl.load_key)(key_input); local sign = impl.sign; + local default_ttl = (options and options.default_ttl) or 3600; return function (payload) + local issued_at; + if not payload.iat then + issued_at = os.time(); + payload.iat = issued_at; + end + if not payload.exp then + payload.exp = (issued_at or os.time()) + default_ttl; + end return sign(key, payload); end end -local function new_verifier(algorithm, key_input) +local function new_verifier(algorithm, key_input, options) local impl = assert(algorithms[algorithm], "Unknown JWT algorithm: "..algorithm); local key = (impl.load_public_key or impl.load_key)(key_input); local verify = impl.verify; + local check_expiry = not (options and options.accept_expired); + local claim_verifier = options and options.claim_verifier; return function (token) - return verify(key, token); + local ok, payload = verify(key, token); + if ok then + local expires_at = check_expiry and payload.exp; + if expires_at then + if type(expires_at) ~= "number" then + return nil, "invalid-expiry"; + elseif expires_at < os.time() then + return nil, "token-expired"; + end + end + if claim_verifier and not claim_verifier(payload) then + return nil, "incorrect-claims"; + end + end + return ok, payload; end end -- cgit v1.2.3 From d2ede10c5d4ed029731f2b431aea3b35da2dd999 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 13:42:08 +0100 Subject: util.jwt: Consolidate payload parsing, ensure it's always a valid object --- util/jwt.lua | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 3501d9f2..53825236 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -33,6 +33,16 @@ local function new_static_header(algorithm_name) return b64url('{"alg":"'..algorithm_name..'","typ":"JWT"}') .. '.'; end +local function decode_raw_payload(raw_payload) + local payload, err = json.decode(unb64url(raw_payload)); + if err ~= nil then + return nil, "json-decode-error"; + elseif type(payload) ~= "table" then + return nil, "invalid-payload-type"; + end + return true, payload; +end + -- HS*** family local function new_hmac_algorithm(name) local static_header = new_static_header(name); @@ -53,11 +63,8 @@ local function new_hmac_algorithm(name) if not secure_equals(b64url(hmac(key, signed)), signature) then return false, "signature-mismatch"; end - local payload, err = json.decode(unb64url(raw_payload)); - if err ~= nil then - return nil, "json-decode-error"; - end - return true, payload; + + return decode_raw_payload(raw_payload); end local function load_key(key) @@ -101,12 +108,7 @@ local function new_crypto_algorithm(name, key_type, c_sign, c_verify, sig_encode return false, "signature-mismatch"; end - local payload, err = json.decode(unb64url(raw_payload)); - if err ~= nil then - return nil, "json-decode-error"; - end - - return true, payload; + return decode_raw_payload(raw_payload); end; load_public_key = function (public_key_pem) -- cgit v1.2.3 From ff54cc5bcfd5fc9ec23f8d974f0c46363f9bb35a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 13:43:01 +0100 Subject: util.jwt: Add new init() convenience method to obtain both signer and verifier --- util/jwt.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 53825236..5c74850a 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -197,9 +197,15 @@ local function new_verifier(algorithm, key_input, options) end end +local function init(algorithm, private_key, public_key, options) + return new_signer(algorithm, private_key, options), new_verifier(algorithm, public_key or private_key, options); +end + return { + init = init; new_signer = new_signer; new_verifier = new_verifier; + -- Exported mainly for tests _algorithms = algorithms; -- Deprecated sign = algorithms.HS256.sign; -- cgit v1.2.3 From e2f61d6e7e2b5862811e22fd7eb065e5997e24e0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 13:51:39 +0100 Subject: util.paseto: Fix to decode footer before comparison --- util/paseto.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua index 5f162ad0..352c9df0 100644 --- a/util/paseto.lua +++ b/util/paseto.lua @@ -69,6 +69,7 @@ function v4_public.verify(tok, pk, expected_f, i) if not h then return nil, "invalid-token-format"; end + f = f and unb64url(f) or nil; if expected_f then if not f or not secure_equals(expected_f, f) then return nil, "invalid-footer"; -- cgit v1.2.3 From 2db2476824b14ee17f28d0427f8e3b56a5b77a5c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 14:08:55 +0100 Subject: util.paseto: Error early on invalid keys --- util/paseto.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua index 352c9df0..c658902c 100644 --- a/util/paseto.lua +++ b/util/paseto.lua @@ -106,6 +106,7 @@ end function v4_public.import_public_key(pem) local key = crypto.import_public_pem(pem); + assert(key:get_type() == "ED25519", "Invalid public key type for v4.public"); return setmetatable({ key = key; }, v4_public_pubkey_mt); @@ -113,6 +114,7 @@ end function v4_public.import_private_key(pem) local key = crypto.import_private_pem(pem); + assert(key:get_type() == "ED25519", "Invalid private key type for v4.public"); return setmetatable({ key = key; }, v4_public_privkey_mt); -- cgit v1.2.3 From 4e4c2dd6dc9a7e63070d5ee57332c359d432a4c3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 14:09:16 +0100 Subject: util.paseto: Export similar API to new util.jwt for ease and consistency --- util/paseto.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua index c658902c..02a0aa70 100644 --- a/util/paseto.lua +++ b/util/paseto.lua @@ -120,6 +120,27 @@ function v4_public.import_private_key(pem) }, v4_public_privkey_mt); end +function v4_public.init(private_key_pem, public_key_pem, options) + local sign, verify = v4_public.sign, v4_public.verify; + local public_key = public_key_pem and v4_public.import_public_key(public_key_pem); + local private_key = private_key_pem and v4_public.import_private_key(private_key_pem); + local default_footer = options and options.default_footer; + local default_assertion = options and options.default_implicit_assertion; + return private_key and function (token, token_footer, token_assertion) + return sign(token, private_key, token_footer or default_footer, token_assertion or default_assertion); + end, public_key and function (token, expected_footer, token_assertion) + return verify(token, public_key, expected_footer or default_footer, token_assertion or default_assertion); + end; +end + +function v4_public.new_signer(private_key_pem, options) + return (v4_public.init(private_key_pem, nil, options)); +end + +function v4_public.new_verifier(public_key_pem, options) + return (select(2, v4_public.init(public_key_pem, options))); +end + return { pae = pae; v4_public = v4_public; -- cgit v1.2.3 From e12c9a83df999049970403cbbd6a6427f79f889e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 11 Jul 2022 14:30:39 +0100 Subject: util.paseto: Drop custom wrappers around key objects The PASETO spec recommends - no, *requires* - that implementations enforce type safety for keys, and e.g. do not pass them around as arbitrary byte strings. Typed wrapper objects are recommended. I originally followed this advice when starting the lib. However, key wrapping and type safety is now also a feature of util.crypto. All we're doing is duplicating it unnecessarily with this additional wrapper code. --- util/paseto.lua | 51 +++++---------------------------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua index 02a0aa70..a39f9667 100644 --- a/util/paseto.lua +++ b/util/paseto.lua @@ -8,11 +8,6 @@ local s_pack = require "util.struct".pack; local s_gsub = string.gsub; -local pubkey_methods = {}; -local privkey_methods = {}; - -local v4_public_pubkey_mt = { __index = pubkey_methods }; -local v4_public_privkey_mt = { __index = privkey_methods }; local v4_public = {}; local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" }; @@ -35,25 +30,14 @@ local function pae(parts) return table.concat(o); end -function privkey_methods:export() - return self.key:private_pem(); -end - -function pubkey_methods:export() - return self.key:public_pem(); -end - function v4_public.sign(m, sk, f, i) - if getmetatable(sk) ~= v4_public_privkey_mt then - error("cannot sign v4.public tokens with this key"); - end if type(m) ~= "table" then return nil, "PASETO payloads must be a table"; end m = json.encode(m); local h = "v4.public."; local m2 = pae({ h, m, f or "", i or "" }); - local sig = crypto.ed25519_sign(sk.key, m2); + local sig = crypto.ed25519_sign(sk, m2); if not f or f == "" then return h..b64url(m..sig); else @@ -62,9 +46,6 @@ function v4_public.sign(m, sk, f, i) end function v4_public.verify(tok, pk, expected_f, i) - if getmetatable(pk) ~= v4_public_pubkey_mt then - error("cannot verify v4.public tokens with this key"); - end local h, sm, f = tok:match("^(v4%.public%.)([^%.]+)%.?(.*)$"); if not h then return nil, "invalid-token-format"; @@ -81,7 +62,7 @@ function v4_public.verify(tok, pk, expected_f, i) end local s, m = raw_sm:sub(-64), raw_sm:sub(1, -65); local m2 = pae({ h, m, f or "", i or "" }); - local ok = crypto.ed25519_verify(pk.key, m2, s); + local ok = crypto.ed25519_verify(pk, m2, s); if not ok then return nil, "invalid-token"; end @@ -92,32 +73,10 @@ function v4_public.verify(tok, pk, expected_f, i) return payload; end +v4_public.import_private_key = crypto.import_private_pem; +v4_public.import_public_key = crypto.import_public_pem; function v4_public.new_keypair() - local key = crypto.generate_ed25519_keypair(); - return { - private_key = setmetatable({ - key = key; - }, v4_public_privkey_mt); - public_key = setmetatable({ - key = key; - }, v4_public_pubkey_mt); - }; -end - -function v4_public.import_public_key(pem) - local key = crypto.import_public_pem(pem); - assert(key:get_type() == "ED25519", "Invalid public key type for v4.public"); - return setmetatable({ - key = key; - }, v4_public_pubkey_mt); -end - -function v4_public.import_private_key(pem) - local key = crypto.import_private_pem(pem); - assert(key:get_type() == "ED25519", "Invalid private key type for v4.public"); - return setmetatable({ - key = key; - }, v4_public_privkey_mt); + return crypto.generate_ed25519_keypair(); end function v4_public.init(private_key_pem, public_key_pem, options) -- cgit v1.2.3 From a3e182ffa1718472d8bae11b27004f6c6311b4a3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 Jul 2022 18:48:57 +0200 Subject: util.paseto: Do strict type check in pae() function Fixes a test failure on Lua 5.4 where ipairs("") does not produce an error. --- util/paseto.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/paseto.lua b/util/paseto.lua index a39f9667..8b564c96 100644 --- a/util/paseto.lua +++ b/util/paseto.lua @@ -23,6 +23,9 @@ local function le64(n) end local function pae(parts) + if type(parts) ~= "table" then + error("bad argument #1 to 'pae' (table expected, got "..type(parts)..")"); + end local o = { le64(#parts) }; for _, part in ipairs(parts) do table.insert(o, le64(#part)..part); -- cgit v1.2.3 From 8695a72a668fa38f1df64653508c360534e5e3db Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 29 Sep 2022 23:15:39 +0100 Subject: util.crypto, util.jwt: Generate consistent signature sizes (via padding) This fixes the signature parsing and building to work correctly. Sometimes a signature was one or two bytes too short, and needed to be padded. OpenSSL can do this for us. --- util/jwt.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 5c74850a..1c7daf12 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -135,21 +135,21 @@ local function new_rsa_algorithm(name) end -- ES*** -local function new_ecdsa_algorithm(name, c_sign, c_verify) +local function new_ecdsa_algorithm(name, c_sign, c_verify, sig_bytes) local function encode_ecdsa_sig(der_sig) - local r, s = crypto.parse_ecdsa_signature(der_sig); + local r, s = crypto.parse_ecdsa_signature(der_sig, sig_bytes); return r..s; end local function decode_ecdsa_sig(jwk_sig) - return crypto.build_ecdsa_signature(jwk_sig:sub(1, 32), jwk_sig:sub(33, 64)); + return crypto.build_ecdsa_signature(jwk_sig:sub(1, sig_bytes), jwk_sig:sub(sig_bytes+1, sig_bytes*2)); end return new_crypto_algorithm(name, "id-ecPublicKey", c_sign, c_verify, encode_ecdsa_sig, decode_ecdsa_sig); end local algorithms = { HS256 = new_hmac_algorithm("HS256"), HS384 = new_hmac_algorithm("HS384"), HS512 = new_hmac_algorithm("HS512"); - ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify); + ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify, 32); RS256 = new_rsa_algorithm("RS256"), RS384 = new_rsa_algorithm("RS384"), RS512 = new_rsa_algorithm("RS512"); PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512"); }; -- cgit v1.2.3 From 5ca81b0e3bcef02ebf6e518e10a855c6ed3efd7f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 29 Sep 2022 23:17:42 +0100 Subject: util.jwt: Add support for ES512 (+ tests) --- util/jwt.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 1c7daf12..0c878efb 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -150,6 +150,7 @@ end local algorithms = { HS256 = new_hmac_algorithm("HS256"), HS384 = new_hmac_algorithm("HS384"), HS512 = new_hmac_algorithm("HS512"); ES256 = new_ecdsa_algorithm("ES256", crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify, 32); + ES512 = new_ecdsa_algorithm("ES512", crypto.ecdsa_sha512_sign, crypto.ecdsa_sha512_verify, 66); RS256 = new_rsa_algorithm("RS256"), RS384 = new_rsa_algorithm("RS384"), RS512 = new_rsa_algorithm("RS512"); PS256 = new_rsa_algorithm("PS256"), PS384 = new_rsa_algorithm("PS384"), PS512 = new_rsa_algorithm("PS512"); }; -- cgit v1.2.3 From e7bfb40a324c806c350f4c87cd41e3b83033377e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 30 Sep 2022 20:38:31 +0100 Subject: util.jwt: More robust ECDSA signature parsing, fail early on unexpected length --- util/jwt.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua index 0c878efb..42a9f7f2 100644 --- a/util/jwt.lua +++ b/util/jwt.lua @@ -141,8 +141,12 @@ local function new_ecdsa_algorithm(name, c_sign, c_verify, sig_bytes) return r..s; end + local expected_sig_length = sig_bytes*2; local function decode_ecdsa_sig(jwk_sig) - return crypto.build_ecdsa_signature(jwk_sig:sub(1, sig_bytes), jwk_sig:sub(sig_bytes+1, sig_bytes*2)); + if #jwk_sig ~= expected_sig_length then + return nil; + end + return crypto.build_ecdsa_signature(jwk_sig:sub(1, sig_bytes), jwk_sig:sub(sig_bytes+1)); end return new_crypto_algorithm(name, "id-ecPublicKey", c_sign, c_verify, encode_ecdsa_sig, decode_ecdsa_sig); end -- cgit v1.2.3 From bfa6d5634afbed156537de9eb620964b24111ff2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 6 Oct 2022 11:12:57 +0100 Subject: prosodyctl: check turn: More clearly indicate the error is from TURN server --- util/prosodyctl/check.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua index 42d73f29..3f89f930 100644 --- a/util/prosodyctl/check.lua +++ b/util/prosodyctl/check.lua @@ -155,7 +155,7 @@ local function check_turn_service(turn_service, ping_service) result.error = "TURN server did not response to allocation request: "..err; return result; elseif alloc_response:is_err_resp() then - result.error = ("TURN allocation failed: %d (%s)"):format(alloc_response:get_error()); + result.error = ("TURN server failed to create allocation: %d (%s)"):format(alloc_response:get_error()); return result; elseif not alloc_response:is_success_resp() then result.error = ("Unexpected TURN response: %d (%s)"):format(alloc_response:get_type()); -- cgit v1.2.3 From 08b49654d9bfd6a280e336d834f4bcfee8bda8e1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 6 Oct 2022 18:34:40 +0100 Subject: util.iterators: join: Work even with only a single iterator in the chain --- util/iterators.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/iterators.lua b/util/iterators.lua index 4529697a..eb4c54af 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -240,7 +240,8 @@ function join_methods:prepend(f, s, var) end function it.join(f, s, var) - return setmetatable({ {f, s, var} }, join_mt); + local t = setmetatable({ {f, s, var} }, join_mt); + return t, { t, 1 }; end return it; -- cgit v1.2.3 From d6863b51b94b1a9e5e4cf8ad18a5804807dffe8f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 7 Oct 2022 16:58:08 +0100 Subject: util.roles: Return nil if the role has no explicit policy (fixes inheritance) Previously, if the first inherited role had no opinion, it returned false and prevented further consultation of other inherited roles. This bug was found thanks to the implementation of missing test cases identified through mutation testing. --- util/roles.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/roles.lua b/util/roles.lua index 831ce6ae..a38faa58 100644 --- a/util/roles.lua +++ b/util/roles.lua @@ -30,7 +30,7 @@ local function _new_may(permissions, inherited_mays) end end end - return false; + return nil; end end -- cgit v1.2.3 From d08ddc1f4a1c6d93342bd8efd69c837782656f1f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 7 Oct 2022 17:01:35 +0100 Subject: util.roles: Add a :policies() method to iterate through available policies We don't expose the policies directly, to force people to go through :may(). However, there are times when we really just need to know what policies a role has inside it (e.g. for reporting or debugging purposes). --- util/roles.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'util') diff --git a/util/roles.lua b/util/roles.lua index a38faa58..2c3a5026 100644 --- a/util/roles.lua +++ b/util/roles.lua @@ -86,6 +86,16 @@ function role_methods:set_permission(permission_name, policy, overwrite) return true; end +function role_methods:policies() + local policy_iterator, s, v = it.join(pairs(self[permissions_key])); + if self.inherits then + for _, inherited_role in ipairs(self.inherits) do + policy_iterator:append(inherited_role:policies()); + end + end + return policy_iterator, s, v; +end + function role_mt.__tostring(self) return ("role<[%s] %s>"):format(self.id or "nil", self.name or "[no name]"); end -- cgit v1.2.3 From aed0c1c5ababe8b44ac8f88ef423b641c0c6ec4f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 7 Oct 2022 17:43:26 +0100 Subject: util.promise: Remove some redundant checks, add tests confirming redundancy This lines don't appear to do anything useful, and all tests pass when they are removed. Discovered via mutation testing. I added extra tests to exercise this code, because I wasn't certain that there were no side-effects caused by removal. Everything appears to be fine, thanks to the "pending" check at the start of promise_settle(). --- util/promise.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'util') diff --git a/util/promise.lua b/util/promise.lua index 1762d501..d8f0b3d6 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -57,9 +57,7 @@ local function promise_settle(promise, new_state, new_next, cbs, value) end local function new_resolve_functions(p) - local resolved = false; local function _resolve(v) - if resolved then return; end resolved = true; if is_promise(v) then v:next(new_resolve_functions(p)); @@ -69,8 +67,6 @@ local function new_resolve_functions(p) end local function _reject(e) - if resolved then return; end - resolved = true; if promise_settle(p, "rejected", next_rejected, p._pending_on_rejected, e) then p.reason = e; end -- cgit v1.2.3 From a79238fb37eb9ad993e906fb834e2f7696d70b11 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 7 Oct 2022 17:45:23 +0100 Subject: util.promise: Remove line that was supposed to be removed in eb9814372c54 --- util/promise.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'util') diff --git a/util/promise.lua b/util/promise.lua index d8f0b3d6..f56502d2 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -58,7 +58,6 @@ end local function new_resolve_functions(p) local function _resolve(v) - resolved = true; if is_promise(v) then v:next(new_resolve_functions(p)); elseif promise_settle(p, "fulfilled", next_fulfilled, p._pending_on_fulfilled, v) then -- cgit v1.2.3 From 586a0d8493acfd511a2d2319ad68628d585f94df Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 11 Oct 2022 11:34:47 +0100 Subject: util.dbuffer: Remove redundant code (read_chunk() cannot fail at this point) --- util/dbuffer.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 3ad5fdfe..6c671fa3 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -96,13 +96,9 @@ function dbuffer_methods:discard(requested_bytes) end local chunk, read_bytes = self:read_chunk(requested_bytes); - if chunk then - requested_bytes = requested_bytes - read_bytes; - if requested_bytes == 0 then -- Already read everything we need - return true; - end - else - return nil; + requested_bytes = requested_bytes - read_bytes; + if requested_bytes == 0 then -- Already read everything we need + return true; end while chunk do -- cgit v1.2.3 From ffa72d829be2b988074f269fc5a2177b74c77ab4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 11 Oct 2022 11:37:55 +0100 Subject: util.dbuffer: Add efficient shortcuts for discard() in certain cases If the buffer is already empty, nothing to do. If we're throwing away the whole buffer, we can just empty it and avoid read_chunk() (which in turn may collapse()). These shortcuts are much more efficient. --- util/dbuffer.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 6c671fa3..0a36288d 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -91,8 +91,12 @@ function dbuffer_methods:read_until(char) end function dbuffer_methods:discard(requested_bytes) - if requested_bytes > self._length then - return nil; + if self._length == 0 then return true; end + if not requested_bytes or requested_bytes >= self._length then + self.front_consumed = 0; + self._length = 0; + for _ in self.items:consume() do end + return true; end local chunk, read_bytes = self:read_chunk(requested_bytes); -- cgit v1.2.3 From 6ae850c963db802a606b2fd11d670e599a43626f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 11 Oct 2022 13:33:19 +0100 Subject: util.jid: Remove redundant check from split() (micro-optimization?) --- util/jid.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/jid.lua b/util/jid.lua index 759af746..3e1336fc 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -35,8 +35,7 @@ local function split(jid) if jid == nil then return; end local node, nodepos = match(jid, "^([^@/]+)@()"); local host, hostpos = match(jid, "^([^@/]+)()", nodepos); - if node ~= nil and host == nil then return nil, nil, nil; end - local resource = match(jid, "^/(.+)$", hostpos); + local resource = host and match(jid, "^/(.+)$", hostpos); if (host == nil) or ((resource == nil) and #jid >= hostpos) then return nil, nil, nil; end return node, host, resource; end -- cgit v1.2.3 From 120e01f16248ddc9148b96eef5e061be7ce850be Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 11 Oct 2022 13:35:09 +0100 Subject: util.jid: Simplify boolean logic in conditionals --- util/jid.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/jid.lua b/util/jid.lua index 3e1336fc..55567ea2 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -90,9 +90,9 @@ local function compare(jid, acl) -- TODO compare to table of rules? local jid_node, jid_host, jid_resource = split(jid); local acl_node, acl_host, acl_resource = split(acl); - if ((acl_node ~= nil and acl_node == jid_node) or acl_node == nil) and - ((acl_host ~= nil and acl_host == jid_host) or acl_host == nil) and - ((acl_resource ~= nil and acl_resource == jid_resource) or acl_resource == nil) then + if (acl_node == nil or acl_node == jid_node) and + (acl_host == nil or acl_host == jid_host) and + (acl_resource == nil or acl_resource == jid_resource) then return true end return false -- cgit v1.2.3 From e64c5e30c2ab1f59c5051f2bd66f053d34a6eb25 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 19 Oct 2022 16:25:05 +0200 Subject: util.startup: Provide a common Lua 5.3+ math.type() for Lua 5.2 Code deduplication --- util/format.lua | 4 +--- util/jsonpointer.lua | 4 +--- util/serialization.lua | 4 +--- util/startup.lua | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 203bdeab..0631f423 100644 --- a/util/format.lua +++ b/util/format.lua @@ -11,9 +11,7 @@ local pack = table.pack; local valid_utf8 = require "util.encodings".utf8.valid; local type = type; local dump = require "util.serialization".new("debug"); -local num_type = math.type or function (n) - return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; -end +local num_type = math.type; -- In Lua 5.3+ these formats throw an error if given a float local expects_integer = { c = true, d = true, i = true, o = true, u = true, X = true, x = true, }; diff --git a/util/jsonpointer.lua b/util/jsonpointer.lua index 9b871ae7..f1c354a4 100644 --- a/util/jsonpointer.lua +++ b/util/jsonpointer.lua @@ -1,6 +1,4 @@ -local m_type = math.type or function (n) - return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; -end; +local m_type = math.type; local function unescape_token(escaped_token) local unescaped = escaped_token:gsub("~1", "/"):gsub("~0", "~") diff --git a/util/serialization.lua b/util/serialization.lua index d310a3e8..6552d53b 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -22,9 +22,7 @@ local pcall = pcall; local envload = require"util.envload".envload; local pos_inf, neg_inf = math.huge, -math.huge; -local m_type = math.type or function (n) - return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; -end; +local m_type = math.type; local function rawpairs(t) return next, t, nil; diff --git a/util/startup.lua b/util/startup.lua index 10ff1875..9e512cb3 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -277,6 +277,20 @@ function startup.init_global_state() startup.detect_platform(); startup.detect_installed(); _G.prosody = prosody; + + -- COMPAT Lua < 5.3 + if not math.type then + -- luacheck: ignore 122/math + function math.type(n) + if type(n) == "number" then + if n % 1 == 0 and (n + 1 ~= n and n - 1 ~= n) then + return "integer" + else + return "float" + end + end + end + end end function startup.setup_datadir() -- cgit v1.2.3 From e2cff346410c57686478d68fabd96cf4247df927 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Oct 2022 16:50:12 +0200 Subject: util.mathcompat: Module to ease reuse of math.type() Mostly to ensure it is available during tests, as util.startup is not invoked there --- util/datamapper.lua | 4 ++++ util/mathcompat.lua | 13 +++++++++++++ util/serialization.lua | 4 ++++ util/startup.lua | 11 +---------- 4 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 util/mathcompat.lua (limited to 'util') diff --git a/util/datamapper.lua b/util/datamapper.lua index 2378314c..e1484525 100644 --- a/util/datamapper.lua +++ b/util/datamapper.lua @@ -1,5 +1,9 @@ -- This file is generated from teal-src/util/datamapper.lua +if not math.type then + require("util.mathcompat") +end + local st = require("util.stanza"); local pointer = require("util.jsonpointer"); diff --git a/util/mathcompat.lua b/util/mathcompat.lua new file mode 100644 index 00000000..e8acb261 --- /dev/null +++ b/util/mathcompat.lua @@ -0,0 +1,13 @@ +if not math.type then + + local function math_type(t) + if type(t) == "number" then + if t % 1 == 0 and t ~= t + 1 and t ~= t - 1 then + return "integer" + else + return "float" + end + end + end + _G.math.type = math_type +end diff --git a/util/serialization.lua b/util/serialization.lua index 6552d53b..e2e104f1 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -21,6 +21,10 @@ local to_hex = require "util.hex".to; local pcall = pcall; local envload = require"util.envload".envload; +if not math.type then + require "util.mathcompat" +end + local pos_inf, neg_inf = math.huge, -math.huge; local m_type = math.type; diff --git a/util/startup.lua b/util/startup.lua index 9e512cb3..68b74984 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -280,16 +280,7 @@ function startup.init_global_state() -- COMPAT Lua < 5.3 if not math.type then - -- luacheck: ignore 122/math - function math.type(n) - if type(n) == "number" then - if n % 1 == 0 and (n + 1 ~= n and n - 1 ~= n) then - return "integer" - else - return "float" - end - end - end + require "util.mathcompat" end end -- cgit v1.2.3 From 1acd5e0474c00f29fa436eba61c7aceb65ed08dd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Oct 2022 17:35:01 +0200 Subject: util.human.io: Fix handling of os.execute() return values in Lua 5.2+ Wrong part of Lua 5.1 compat removed in 0f4feaf9ca64 --- util/human/io.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 4fce0e94..b272af71 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -8,7 +8,7 @@ end; local function getchar(n) local stty_ret = os.execute("stty raw -echo 2>/dev/null"); local ok, char; - if stty_ret == true or stty_ret == 0 then + if stty_ret then ok, char = pcall(io.read, n or 1); os.execute("stty sane"); else @@ -31,11 +31,11 @@ end local function getpass() local stty_ret = os.execute("stty -echo 2>/dev/null"); - if stty_ret ~= 0 then + if not stty_ret then io.write("\027[08m"); -- ANSI 'hidden' text attribute end local ok, pass = pcall(io.read, "*l"); - if stty_ret == 0 then + if stty_ret then os.execute("stty sane"); else io.write("\027[00m"); -- cgit v1.2.3 From 640b8d49a302b8c5b2ecbc9a8464b0d409eade5d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Oct 2022 17:36:05 +0200 Subject: util.openssl: Remove Lua 5.1 os.execute() return value compat --- util/openssl.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/openssl.lua b/util/openssl.lua index 32b5aea7..3acb4f04 100644 --- a/util/openssl.lua +++ b/util/openssl.lua @@ -166,8 +166,7 @@ do -- Lua to shell calls. setmetatable(_M, { __index = function(_, command) return function(opts) - local ret = os_execute(serialize(command, type(opts) == "table" and opts or {})); - return ret == true or ret == 0; + return os_execute(serialize(command, type(opts) == "table" and opts or {})); end; end; }); -- cgit v1.2.3 From e673312b7f9142c0f8d50daf8de11eb398d5e1e9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Oct 2022 17:37:07 +0200 Subject: util.prosodyctl: Remove Lua 5.1 os.execute() return value compat --- util/prosodyctl.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 4d49cd16..b3163799 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -224,8 +224,7 @@ local function call_luarocks(operation, mod, server) local ok, _, code = os.execute(render_cli("luarocks --lua-version={luav} {op} --tree={dir} {server&--server={server}} {mod?}", { dir = dir; op = operation; mod = mod; server = server; luav = _VERSION:match("5%.%d"); })); - if type(ok) == "number" then code = ok; end - return code; + return ok and code; end return { -- cgit v1.2.3 From 03814250c300b8cbaaccfde6bdb856039c3a4691 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Oct 2022 17:37:33 +0200 Subject: util.prosodyctl.cert: Remove Lua 5.1 os.execute() return value compat --- util/prosodyctl/cert.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/cert.lua b/util/prosodyctl/cert.lua index 02c81585..ebc14a4e 100644 --- a/util/prosodyctl/cert.lua +++ b/util/prosodyctl/cert.lua @@ -179,7 +179,7 @@ local function copy(from, to, umask, owner, group) os.execute(("chown -c --reference=%s %s"):format(sh_esc(cert_basedir), sh_esc(to))); elseif owner and group then local ok = os.execute(("chown %s:%s %s"):format(sh_esc(owner), sh_esc(group), sh_esc(to))); - assert(ok == true or ok == 0, "Failed to change ownership of "..to); + assert(ok, "Failed to change ownership of "..to); end if old_umask then pposix.umask(old_umask); end return true; -- cgit v1.2.3 From ae84717255cb196013c1b0fe0a625b2b443e7dc4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 2 Dec 2022 20:32:36 +0000 Subject: util.hashring: Support associating arbitrary data with nodes In this API, a 'node' is always a simple text string. Sometimes the caller may have a more complex structure representing a node, but the hash ring is really only concerned with the node's name. This API change allows :add_nodes() to take a table of `node_name = value` pairs, as well as the simple array of node names previously accepted. The 'value' of the selected node is returned as a new second result from :get_node(). If no value is passed when a node is added, it defaults to `true` (as before, but this was never previously exposed). --- util/hashring.lua | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/hashring.lua b/util/hashring.lua index d4555669..5e71654b 100644 --- a/util/hashring.lua +++ b/util/hashring.lua @@ -1,3 +1,5 @@ +local it = require "util.iterators"; + local function generate_ring(nodes, num_replicas, hash) local new_ring = {}; for _, node_name in ipairs(nodes) do @@ -28,18 +30,22 @@ local function new(num_replicas, hash_function) return setmetatable({ nodes = {}, num_replicas = num_replicas, hash = hash_function }, hashring_mt); end; -function hashring_methods:add_node(name) +function hashring_methods:add_node(name, value) self.ring = nil; - self.nodes[name] = true; + self.nodes[name] = value == nil and true or value; table.insert(self.nodes, name); return true; end function hashring_methods:add_nodes(nodes) self.ring = nil; - for _, node_name in ipairs(nodes) do - if not self.nodes[node_name] then - self.nodes[node_name] = true; + local iter = pairs; + if nodes[1] then -- simple array? + iter = it.values; + end + for node_name, node_value in iter(nodes) do + if self.nodes[node_name] == nil then + self.nodes[node_name] = node_value == nil and true or node_value; table.insert(self.nodes, node_name); end end @@ -48,7 +54,7 @@ end function hashring_methods:remove_node(node_name) self.ring = nil; - if self.nodes[node_name] then + if self.nodes[node_name] ~= nil then for i, stored_node_name in ipairs(self.nodes) do if node_name == stored_node_name then self.nodes[node_name] = nil; @@ -69,18 +75,26 @@ end function hashring_methods:clone() local clone_hashring = new(self.num_replicas, self.hash); - clone_hashring:add_nodes(self.nodes); + for node_name, node_value in pairs(self.nodes) do + clone_hashring.nodes[node_name] = node_value; + end + clone_hashring.ring = nil; return clone_hashring; end function hashring_methods:get_node(key) + local node; local key_hash = self.hash(key); for _, replica_hash in ipairs(self.ring) do if key_hash < replica_hash then - return self.ring[replica_hash]; + node = self.ring[replica_hash]; + break; end end - return self.ring[self.ring[1]]; + if not node then + node = self.ring[self.ring[1]]; + end + return node, self.nodes[node]; end return { -- cgit v1.2.3