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
This commit is contained in:
parent
87c935379b
commit
be3b9614d0
38 changed files with 280 additions and 5892 deletions
139
src/auth.lua
Normal file
139
src/auth.lua
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
-- 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue