furt/src/auth.lua
michael be3b9614d0 refactor: clean repository structure for v0.1.0 open source release
- Remove Go artifacts (cmd/, internal/, pkg/, go.mod)
- Move furt-lua/* content to repository root
- Restructure as clean src/, config/, scripts/, tests/ layout
- Rewrite README.md as practical tool documentation
- Remove timeline references and marketing language
- Clean .gitignore from Go-era artifacts
- Update config/server.lua with example.org defaults
- Add .env.production to .gitignore for security

Repository now ready for open source distribution with minimal,
focused structure and generic configuration templates.
close issue DAW/furt#86
2025-08-14 09:36:55 +02:00

139 lines
4.3 KiB
Lua

-- furt-lua/src/auth.lua
-- API Key authentication system
-- Dragons@Work Digital Sovereignty Project
local IpUtils = require("src.ip_utils")
local RateLimiter = require("src.rate_limiter")
local Auth = {}
-- Load configuration
local config = require("config.server")
-- Authenticate incoming request
function Auth.authenticate_request(request)
local api_key = request.headers["x-api-key"]
if not api_key then
return false, "Missing X-API-Key header", 401
end
-- Check if API key exists in config
local key_config = config.api_keys and config.api_keys[api_key]
if not key_config then
return false, "Invalid API key", 401
end
-- Get client IP
local client_ip = IpUtils.get_client_ip(request)
-- Check IP restrictions
if not IpUtils.is_ip_allowed(client_ip, key_config.allowed_ips) then
return false, "IP address not allowed", 403
end
-- Check rate limits
local rate_ok, rate_message, rate_info = RateLimiter:check_api_and_ip_limits(api_key, client_ip)
if not rate_ok then
return false, rate_message, 429, rate_info
end
-- Return auth context
return true, {
api_key = api_key,
key_name = key_config.name,
permissions = key_config.permissions or {},
client_ip = client_ip,
rate_info = rate_info
}
end
-- Check if user has specific permission
function Auth.has_permission(auth_context, required_permission)
if not auth_context or not auth_context.permissions then
return false
end
-- No permission required = always allow for authenticated users
if not required_permission then
return true
end
-- Check for specific permission or wildcard
for _, permission in ipairs(auth_context.permissions) do
if permission == required_permission or permission == "*" then
return true
end
end
return false
end
-- Create auth middleware wrapper for route handlers
function Auth.create_protected_route(required_permission, handler)
return function(request, server)
-- Authenticate request
local auth_success, auth_result, status_code, rate_info = Auth.authenticate_request(request)
if not auth_success then
local error_response = {
error = auth_result,
timestamp = os.time()
}
-- Add rate limit info to error if available
if rate_info then
error_response.rate_limit = rate_info
end
return server:create_response(status_code or 401, error_response, nil, nil, request)
end
-- Check permissions
if required_permission and not Auth.has_permission(auth_result, required_permission) then
return server:create_response(403, {
error = "Insufficient permissions",
required = required_permission,
available = auth_result.permissions
}, nil, nil, request)
end
-- Add auth context to request
request.auth = auth_result
-- Get rate limit headers
local rate_headers = RateLimiter:get_rate_limit_headers(auth_result.rate_info)
-- Call original handler
local result = handler(request, server)
-- If result is a string (already formatted response), return as-is
if type(result) == "string" then
return result
end
-- If handler returned data, create response with rate limit headers
return server:create_response(200, result, "application/json", rate_headers, request)
end
end
-- Get authentication status for debug/monitoring
function Auth.get_auth_status(auth_context)
if not auth_context then
return {
authenticated = false
}
end
return {
authenticated = true,
api_key_name = auth_context.key_name,
permissions = auth_context.permissions,
client_ip = auth_context.client_ip,
rate_limit_remaining = auth_context.rate_info and auth_context.rate_info.api_key and auth_context.rate_info.api_key.remaining,
ip_rate_limit_remaining = auth_context.rate_info and auth_context.rate_info.ip and auth_context.rate_info.ip.remaining
}
end
return Auth