From eefdecb1e9f38f6af8dd28ca3b586438b53669e8 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 6 Dec 2022 13:48:09 -0300 Subject: [PATCH 1/5] use request uri and regex to choose callbacks --- main.lua | 42 +++++++++++++++++++++------------ scripts/webfinger_allowlist.lua | 6 ++++- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/main.lua b/main.lua index ed7677f..e514ab3 100644 --- a/main.lua +++ b/main.lua @@ -12,33 +12,45 @@ function log(msg) end local WANTED_SCRIPTS = { - 'scripts.webfinger_allowlist' + ['scripts.webfinger_allowlist'] = {accounts = {"example@example.com"}} } local compiled_chain = {} -for _, module_name in pairs(WANTED_SCRIPTS) do - log('load module', module_name) - mod = require(module_name) - log('load module', mod) - table.insert(compiled_chain, mod) +for module_name, module_config in pairs(WANTED_SCRIPTS) do + module = require(module_name) + table.insert(compiled_chain, {module, module_config}) end local function onRequest() + local request_uri = ngx.var.uri + + -- find out which modules to call based on their regexes + local callbacks_to_call = {} + for _, filter in ipairs(compiled_chain) do + local module, module_config = filter[1], filter[2] + + for callback_regex, callback_function in pairs(module.callbacks) do + local match, error = ngx.re.match(request_uri, callback_regex) + if match then + table.insert(callbacks_to_call, {callback_function, module_config}) + end + end + end + log('AWOOOOGA') - for _,mod in ipairs(compiled_chain) do - log(mod) - local mod_config = {accounts = {"a@a.com"}} - local result, body = mod.callback(mod_config) - log(result) - log(body) - if not result then + for _,tuple in ipairs(callbacks_to_call) do + local callback_function, config = tuple[1], tuple[2] + local result, body = callback_function(config) + log(result) + log(body) + if not result then ngx.status = 400 ngx.say(body or "request denied") ngx.exit(400) - end - end + end + end end return onRequest diff --git a/scripts/webfinger_allowlist.lua b/scripts/webfinger_allowlist.lua index c036494..bb6c561 100644 --- a/scripts/webfinger_allowlist.lua +++ b/scripts/webfinger_allowlist.lua @@ -6,6 +6,11 @@ function webfingerCallback(cfg) local resource = args['resource'] if resource ~= nil then + -- TODO this is O(n) but we can make it O(1) by doing a funny and making + -- cfg.accounts be transformed into keys in a table + -- + -- this would require us to do some setup() callback as well as + -- the request() callback for _, account in ipairs(cfg.accounts) do if resource == account then return true @@ -27,7 +32,6 @@ return { Useful for small instances. ]], apiVersion=1, - callback=webfingerCallback, callbacks = { ['/.well-known/webfinger'] = webfingerCallback }, From a747fe8182be833b31075979ec35d3182696a403 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 6 Dec 2022 14:52:09 -0300 Subject: [PATCH 2/5] add initialization callback for filters --- main.lua | 10 ++++++---- scripts/webfinger_allowlist.lua | 30 +++++++++++++++--------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/main.lua b/main.lua index e514ab3..71699f6 100644 --- a/main.lua +++ b/main.lua @@ -18,8 +18,10 @@ local WANTED_SCRIPTS = { local compiled_chain = {} for module_name, module_config in pairs(WANTED_SCRIPTS) do - module = require(module_name) - table.insert(compiled_chain, {module, module_config}) + local module = require(module_name) + local module_state = module.init(module_config) + -- TODO is it possible to make module_config readonly? + table.insert(compiled_chain, {module, module_config, module_state}) end local function onRequest() @@ -28,12 +30,12 @@ local function onRequest() -- find out which modules to call based on their regexes local callbacks_to_call = {} for _, filter in ipairs(compiled_chain) do - local module, module_config = filter[1], filter[2] + local module, module_config, state = unpack(filter) for callback_regex, callback_function in pairs(module.callbacks) do local match, error = ngx.re.match(request_uri, callback_regex) if match then - table.insert(callbacks_to_call, {callback_function, module_config}) + table.insert(callbacks_to_call, {callback_function, module_config, state}) end end end diff --git a/scripts/webfinger_allowlist.lua b/scripts/webfinger_allowlist.lua index bb6c561..4575e7e 100644 --- a/scripts/webfinger_allowlist.lua +++ b/scripts/webfinger_allowlist.lua @@ -1,24 +1,23 @@ -function webfingerCallback(cfg) +function webfingerInit(cfg) + local accounts_set = {} + for _, account in ipairs(cfg.accounts) do + accounts_set["acct:" .. account] = true + end + return accounts_set +end + +function webfingerCallback(cfg, accounts_set) local args, err = ngx.req.get_uri_args() if err == "truncated" then - return false, 'uri args too long' + return 400, 'uri args too long' end local resource = args['resource'] - if resource ~= nil then - -- TODO this is O(n) but we can make it O(1) by doing a funny and making - -- cfg.accounts be transformed into keys in a table - -- - -- this would require us to do some setup() callback as well as - -- the request() callback - for _, account in ipairs(cfg.accounts) do - if resource == account then - return true - end - end + if accounts_set[resource] then + return nil + else + return 404, "Couldn't find user" end - - return false end return { @@ -32,6 +31,7 @@ return { Useful for small instances. ]], apiVersion=1, + init=webfingerInit, callbacks = { ['/.well-known/webfinger'] = webfingerCallback }, From 06924d10f6f92997096a156701e409ad9725d2c0 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 6 Dec 2022 14:52:20 -0300 Subject: [PATCH 3/5] let filters customize status code and body --- main.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/main.lua b/main.lua index 71699f6..dd25def 100644 --- a/main.lua +++ b/main.lua @@ -43,14 +43,12 @@ local function onRequest() log('AWOOOOGA') for _,tuple in ipairs(callbacks_to_call) do - local callback_function, config = tuple[1], tuple[2] - local result, body = callback_function(config) - log(result) - log(body) - if not result then - ngx.status = 400 + local callback_function, config, state = unpack(tuple) + local status_code, body = callback_function(config, state) + if status_code ~= nil then + ngx.status = status_code ngx.say(body or "request denied") - ngx.exit(400) + ngx.exit(status_code) end end end From 5d21c975ea52e62cf087fa4ff4f98e1be442b28b Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 6 Dec 2022 15:53:20 -0300 Subject: [PATCH 4/5] add test suite --- README.md | 8 ++++ ctx.lua | 48 ++++++++++++++++++++++ main.lua | 49 ++-------------------- test.lua | 77 +++++++++++++++++++++++++++++++++++ tests/webfinger_allowlist.lua | 23 +++++++++++ 5 files changed, 160 insertions(+), 45 deletions(-) create mode 100644 ctx.lua create mode 100644 test.lua create mode 100644 tests/webfinger_allowlist.lua diff --git a/README.md b/README.md index 963ad21..0721263 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,11 @@ on top of ActivityPub implementations. - write test suite - create install instructions +## Testing + +``` +luarocks-5.1 install --local luaunit +luarocks-5.1 install --local lrexlib-PCRE2 +eval (luarocks-5.1 path --bin) +lua5.1 test.lua +``` diff --git a/ctx.lua b/ctx.lua new file mode 100644 index 0000000..98ddcc8 --- /dev/null +++ b/ctx.lua @@ -0,0 +1,48 @@ +function log(msg) + ngx.log(ngx.STDERR, tostring(msg)) +end + +local ctx = {} + +function ctx:setWantedScripts(graph) + self._wanted_scripts = graph +end + +function ctx:loadChain() + self.compiled_chain = {} + for module_name, module_config in pairs(self._wanted_scripts) do + local module = require(module_name) + local module_state = module.init(module_config) + -- TODO is it possible to make module_config readonly? + table.insert(self.compiled_chain, {module, module_config, module_state}) + end +end + +function ctx:onRequest() + local request_uri = ngx.var.uri + + -- find out which modules to call based on their regexes + local callbacks_to_call = {} + for _, filter in ipairs(self.compiled_chain) do + local module, module_config, state = unpack(filter) + + for callback_regex, callback_function in pairs(module.callbacks) do + local match, error = ngx.re.match(request_uri, callback_regex) + if match then + table.insert(callbacks_to_call, {callback_function, module_config, state}) + end + end + end + + for _,tuple in ipairs(callbacks_to_call) do + local callback_function, config, state = unpack(tuple) + local status_code, body = callback_function(config, state) + if status_code ~= nil then + ngx.status = status_code + ngx.say(body or "request denied") + ngx.exit(status_code) + end + end +end + +return ctx diff --git a/main.lua b/main.lua index dd25def..bf117f3 100644 --- a/main.lua +++ b/main.lua @@ -7,50 +7,9 @@ local CONFIG_PATH = ".;/etc/aproxy" -- -- local config = loadConfig() -function log(msg) - ngx.log(ngx.STDERR, tostring(msg)) -end - -local WANTED_SCRIPTS = { +local ctx = require('ctx') +ctx.setWantedScripts({ ['scripts.webfinger_allowlist'] = {accounts = {"example@example.com"}} -} +}) -local compiled_chain = {} - -for module_name, module_config in pairs(WANTED_SCRIPTS) do - local module = require(module_name) - local module_state = module.init(module_config) - -- TODO is it possible to make module_config readonly? - table.insert(compiled_chain, {module, module_config, module_state}) -end - -local function onRequest() - local request_uri = ngx.var.uri - - -- find out which modules to call based on their regexes - local callbacks_to_call = {} - for _, filter in ipairs(compiled_chain) do - local module, module_config, state = unpack(filter) - - for callback_regex, callback_function in pairs(module.callbacks) do - local match, error = ngx.re.match(request_uri, callback_regex) - if match then - table.insert(callbacks_to_call, {callback_function, module_config, state}) - end - end - end - - log('AWOOOOGA') - - for _,tuple in ipairs(callbacks_to_call) do - local callback_function, config, state = unpack(tuple) - local status_code, body = callback_function(config, state) - if status_code ~= nil then - ngx.status = status_code - ngx.say(body or "request denied") - ngx.exit(status_code) - end - end -end - -return onRequest +return ctx:onRequest diff --git a/test.lua b/test.lua new file mode 100644 index 0000000..63d3a08 --- /dev/null +++ b/test.lua @@ -0,0 +1,77 @@ +lu = require('luaunit') +local rex = require('rex_pcre2') + +function createNgx() + local ngx = { + status = nil + } + + local function mockedThing(self, property) + return function(value) + self['_'..property] = value + end + end + + ngx.say = mockedThing(ngx, "say") + ngx.exit = mockedThing(ngx, "exit") + + ngx.log = function (_, msg) + print(msg) + end + + -- only hold data here + ngx.var = {} + + -- request params api + ngx.req = {} + + ngx.req.get_uri_args = function () + return ngx._uri_args + end + + ngx.req.set_uri_args = function (val) + ngx._uri_args = val + end + + -- regex api + ngx.re = {} + ngx.re.match = rex.match + ngx.re.search = rex.find + + return ngx +end + +function resetNgx() + ngx = createNgx() +end +teardownNgx = resetNgx + +function setupFakeRequest(path, options) + ngx.var.uri = path + if options.params then + ngx.req.set_uri_args(options.params) + end +end + +local ctx = require('ctx') +function setupTest(module_require_path, config) + resetNgx() + local module = require(module_require_path) + state = module.init(config) + ctx.compiled_chain = { + {module, config, state} + } + return module +end + + +function onRequest() + ctx:setWantedScripts() + local ctx = require('ctx') + do + ctx:onRequest() + end +end + +require('tests.webfinger_allowlist') +os.exit(lu.LuaUnit.run()) diff --git a/tests/webfinger_allowlist.lua b/tests/webfinger_allowlist.lua new file mode 100644 index 0000000..7999f43 --- /dev/null +++ b/tests/webfinger_allowlist.lua @@ -0,0 +1,23 @@ +TestWebfinger = {} + +function TestWebfinger:setup() + self.mod = setupTest('scripts.webfinger_allowlist', {accounts = {'correct@example.org'}}) +end + +local WEBFINGER_PATH = '/.well-known/webfinger' + +function TestWebfinger:testCorrectAccount() + setupFakeRequest(WEBFINGER_PATH, { params = {resource = 'acct:correct@example.org'} }) + onRequest() + lu.assertIs(ngx.status, nil) +end + +function TestWebfinger:testWrongAccount() + setupFakeRequest(WEBFINGER_PATH, { params = {resource = 'acct:wrong@example.org'} }) + onRequest() + lu.assertIs(ngx.status, 404) +end + +function TestWebfinger:teardown() + teardownNgx() +end From c15abe3c05f741a530a3f79c2c8a79b0fe95f354 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 6 Dec 2022 15:54:16 -0300 Subject: [PATCH 5/5] fix main script usage of ctx --- main.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/main.lua b/main.lua index bf117f3..13ee4f7 100644 --- a/main.lua +++ b/main.lua @@ -8,8 +8,12 @@ local CONFIG_PATH = ".;/etc/aproxy" -- local config = loadConfig() local ctx = require('ctx') -ctx.setWantedScripts({ + +ctx:setWantedScripts({ ['scripts.webfinger_allowlist'] = {accounts = {"example@example.com"}} }) +ctx:loadChain() -return ctx:onRequest +return function() + ctx:onRequest() +end