From 8ec401930c3978e18c2bf4ee7c7afadaafd3a372 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 28 Aug 2025 19:53:30 +0200 Subject: [PATCH] fix(config): lua 5.1 compatibility and multi-tenant validation - Replace goto statements with if-not pattern for Lua 5.1 compatibility - Validate mail config only for API keys with mail:send permissions - Safe display of API key info for monitoring keys without mail config - Fix health check SMTP detection for new config structure - Multi-tenant system tested and working on port 7811 Fixes multi-tenant config parsing, validation, and health checks. Related to DAW/furt#89 --- .gitignore | 1 + config/server.lua | 19 +++++++- src/config_parser.lua | 102 +++++++++++++++++++++++------------------- src/main.lua | 2 +- 4 files changed, 75 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 0edc799..bddee23 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ debug.log config.local.lua config.production.lua +config/furt.conf diff --git a/config/server.lua b/config/server.lua index 54e3a99..f2a05a7 100644 --- a/config/server.lua +++ b/config/server.lua @@ -67,11 +67,28 @@ print(" Default SMTP: " .. (config.smtp_default.host or "not configured")) local api_key_count = 0 for key_name, key_config in pairs(config.api_keys) do api_key_count = api_key_count + 1 + + -- Check if this API key has mail permissions + local has_mail_permission = false + if key_config.permissions then + for _, perm in ipairs(key_config.permissions) do + if perm == "mail:send" or perm == "*" then + has_mail_permission = true + break + end + end + end + local smtp_info = "" if key_config.mail_smtp_host then smtp_info = " (custom SMTP: " .. key_config.mail_smtp_host .. ")" end - print(" API Key: " .. key_config.name .. " -> " .. key_config.mail_to .. smtp_info) + + if has_mail_permission then + print(" API Key: " .. key_config.name .. " -> " .. key_config.mail_to .. smtp_info) + else + print(" API Key: " .. key_config.name .. " (no mail)" .. smtp_info) + end end print(" Total API Keys: " .. api_key_count) diff --git a/src/config_parser.lua b/src/config_parser.lua index 8721b0c..6fa36d5 100644 --- a/src/config_parser.lua +++ b/src/config_parser.lua @@ -1,6 +1,7 @@ -- src/config_parser.lua -- nginx-style configuration parser for Multi-Tenant setup -- Dragons@Work Digital Sovereignty Project +-- Lua 5.1 compatible (no goto statements) local ConfigParser = {} @@ -26,53 +27,48 @@ function ConfigParser.parse_file(config_path) -- Skip empty lines and comments line = line:match("^%s*(.-)%s*$") -- trim whitespace - if line == "" or line:match("^#") then - goto continue - end - - -- Section headers: [section] or [api_key "keyname"] - local section_match = line:match("^%[([^%]]+)%]$") - if section_match then - if section_match:match("^api_key") then - -- Extract API key from [api_key "keyname"] - local key_name = section_match:match('^api_key%s+"([^"]+)"$') - if not key_name then - error(string.format("Invalid api_key section at line %d: %s", line_number, line)) + if not (line == "" or line:match("^#")) then + -- Section headers: [section] or [api_key "keyname"] + local section_match = line:match("^%[([^%]]+)%]$") + if section_match then + if section_match:match("^api_key") then + -- Extract API key from [api_key "keyname"] + local key_name = section_match:match('^api_key%s+"([^"]+)"$') + if not key_name then + error(string.format("Invalid api_key section at line %d: %s", line_number, line)) + end + current_api_key = key_name + current_section = "api_key" + config.api_keys[key_name] = {} + else + current_section = section_match + current_api_key = nil + if not config[current_section] then + config[current_section] = {} + end end - current_api_key = key_name - current_section = "api_key" - config.api_keys[key_name] = {} else - current_section = section_match - current_api_key = nil - if not config[current_section] then - config[current_section] = {} + -- Key-value pairs: key = value + local key, value = line:match("^([^=]+)=(.+)$") + if key and value then + key = key:match("^%s*(.-)%s*$") -- trim + value = value:match("^%s*(.-)%s*$") -- trim + + -- Remove quotes from value if present + value = value:match('^"(.*)"$') or value + + if current_section == "api_key" and current_api_key then + ConfigParser.set_api_key_value(config.api_keys[current_api_key], key, value) + elseif current_section then + ConfigParser.set_config_value(config[current_section], key, value) + else + error(string.format("Key-value pair outside section at line %d: %s", line_number, line)) + end + else + error(string.format("Invalid line format at line %d: %s", line_number, line)) end end - goto continue end - - -- Key-value pairs: key = value - local key, value = line:match("^([^=]+)=(.+)$") - if key and value then - key = key:match("^%s*(.-)%s*$") -- trim - value = value:match("^%s*(.-)%s*$") -- trim - - -- Remove quotes from value if present - value = value:match('^"(.*)"$') or value - - if current_section == "api_key" and current_api_key then - ConfigParser.set_api_key_value(config.api_keys[current_api_key], key, value) - elseif current_section then - ConfigParser.set_config_value(config[current_section], key, value) - else - error(string.format("Key-value pair outside section at line %d: %s", line_number, line)) - end - else - error(string.format("Invalid line format at line %d: %s", line_number, line)) - end - - ::continue:: end file:close() @@ -163,13 +159,25 @@ function ConfigParser.validate_config(config) key_config.allowed_ips = {} -- no IP restrictions end - -- Validate mail configuration - if not key_config.mail_to then - error("API key '" .. key_name .. "' missing mail_to") + -- Validate mail configuration only if API key has mail:send permission + local has_mail_permission = false + if key_config.permissions then + for _, perm in ipairs(key_config.permissions) do + if perm == "mail:send" or perm == "*" then + has_mail_permission = true + break + end + end end - if not key_config.mail_from then - error("API key '" .. key_name .. "' missing mail_from") + if has_mail_permission then + if not key_config.mail_to then + error("API key '" .. key_name .. "' missing mail_to") + end + + if not key_config.mail_from then + error("API key '" .. key_name .. "' missing mail_from") + end end end diff --git a/src/main.lua b/src/main.lua index eee08ef..7e0268c 100644 --- a/src/main.lua +++ b/src/main.lua @@ -285,7 +285,7 @@ server:add_route("GET", "/health", function(request, server) timestamp = os.time(), source = version_info.source, features = { - smtp_configured = config.mail and config.mail.username ~= nil, + smtp_configured = config.smtp_default and config.smtp_default.host ~= nil, auth_enabled = true, rate_limiting = true, merkwerk_integrated = version_info.source == "merkwerk"