mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-28 23:19:33 +00:00
565 lines
19 KiB
Lua
565 lines
19 KiB
Lua
--
|
|
-- (C) 2020-24 - ntop.org
|
|
--
|
|
--
|
|
|
|
if(pragma_once_rest_utils == true) then
|
|
-- io.write(debug.traceback().."\n")
|
|
-- avoid multiple inclusions
|
|
return
|
|
end
|
|
|
|
pragma_once_rest_utils = true
|
|
|
|
local clock_start = os.clock()
|
|
|
|
local dirs = ntop.getDirs()
|
|
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
|
|
|
|
-- require "lua_utils"
|
|
require "ntop_utils"
|
|
require "locales_utils"
|
|
require "lua_utils_generic"
|
|
local json = require("dkjson")
|
|
|
|
local rest_utils = {
|
|
consts = {
|
|
success = {
|
|
ok = {http_code = 200, rc = 0, str = "OK"},
|
|
snmp_device_deleted = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "SNMP_DEVICE_DELETED_SUCCESSFULLY"
|
|
},
|
|
snmp_device_added = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "SNMP_DEVICE_ADDED_SUCCESSFULLY"
|
|
},
|
|
snmp_device_edited = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "SNMP_DEVICE_EDITED_SUCCESSFULLY"
|
|
},
|
|
pool_deleted = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "POOL_DELETED_SUCCESSFULLY"
|
|
},
|
|
pool_added = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "POOL_ADDED_SUCCESSFULLY"
|
|
},
|
|
pool_edited = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "POOL_EDITED_SUCCESSFULLY"
|
|
},
|
|
pool_member_bound = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "POOL_MEMBER_BOUND_SUCCESSFULLY"
|
|
},
|
|
-- infrastructure Dashboard
|
|
infrastructure_instance_added = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "INFRASTRUCTURE_INSTANCE_ADDED"
|
|
},
|
|
infrastructure_instance_edited = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "INFRASTRUCTURE_INSTANCE_EDITED"
|
|
},
|
|
infrastructure_instance_deleted = {
|
|
http_code = 200,
|
|
rc = 0,
|
|
str = "INFRASTRUCTURE_INSTANCE_DELETED"
|
|
}
|
|
},
|
|
err = {
|
|
not_found = {http_code = 404, rc = -1, str = "NOT_FOUND"},
|
|
invalid_interface = {
|
|
http_code = 400,
|
|
rc = -2,
|
|
str = "INVALID_INTERFACE"
|
|
},
|
|
not_granted = {http_code = 401, rc = -3, str = "NOT_GRANTED"},
|
|
invalid_host = {http_code = 400, rc = -4, str = "INVALID_HOST"},
|
|
invalid_args = {http_code = 400, rc = -5, str = "INVALID_ARGUMENTS"},
|
|
internal_error = {http_code = 500, rc = -6, str = "INTERNAL_ERROR"},
|
|
bad_format = {http_code = 400, rc = -7, str = "BAD_FORMAT"},
|
|
bad_content = {http_code = 400, rc = -8, str = "BAD_CONTENT"},
|
|
resolution_failed = {
|
|
http_code = 400,
|
|
rc = -9,
|
|
str = "NAME_RESOLUTION_FAILED"
|
|
},
|
|
snmp_device_already_added = {
|
|
http_code = 409,
|
|
rc = -10,
|
|
str = "SNMP_DEVICE_ALREADY_ADDED"
|
|
},
|
|
snmp_device_unreachable = {
|
|
http_code = 400,
|
|
rc = -11,
|
|
str = "SNMP_DEVICE_UNREACHABLE"
|
|
},
|
|
snmp_device_no_device_discovered = {
|
|
http_code = 400,
|
|
rc = -12,
|
|
str = "NO_SNMP_DEVICE_DISCOVERED"
|
|
},
|
|
add_pool_failed = {
|
|
http_code = 409,
|
|
rc = -13,
|
|
str = "ADD_POOL_FAILED"
|
|
},
|
|
edit_pool_failed = {
|
|
http_code = 409,
|
|
rc = -14,
|
|
str = "EDIT_POOL_FAILED"
|
|
},
|
|
delete_pool_failed = {
|
|
http_code = 409,
|
|
rc = -15,
|
|
str = "DELETE_POOL_FAILED"
|
|
},
|
|
pool_not_found = {http_code = 409, rc = -16, str = "POOL_NOT_FOUND"},
|
|
bind_pool_member_failed = {
|
|
http_code = 409,
|
|
rc = -17,
|
|
str = "BIND_POOL_MEMBER_FAILED"
|
|
},
|
|
bind_pool_member_already_bound = {
|
|
http_code = 409,
|
|
rc = -18,
|
|
str = "BIND_POOL_MEMBER_ALREADY_BOUND"
|
|
},
|
|
password_mismatch = {
|
|
http_code = 400,
|
|
rc = -19,
|
|
str = "PASSWORD_MISMATCH"
|
|
},
|
|
add_user_failed = {
|
|
http_code = 409,
|
|
rc = -20,
|
|
str = "ADD_USER_FAILED"
|
|
},
|
|
delete_user_failed = {
|
|
http_code = 409,
|
|
rc = -21,
|
|
str = "DELETE_USER_FAILED"
|
|
},
|
|
snmp_unknown_device = {
|
|
http_code = 400,
|
|
rc = -22,
|
|
str = "SNMP_UNKNOWN_DEVICE"
|
|
},
|
|
user_already_existing = {
|
|
http_code = 409,
|
|
rc = -23,
|
|
str = "USER_ALREADY_EXISTING"
|
|
},
|
|
user_does_not_exist = {
|
|
http_code = 409,
|
|
rc = -24,
|
|
str = "USER_DOES_NOT_EXIST"
|
|
},
|
|
edit_user_failed = {
|
|
http_code = 400,
|
|
rc = -25,
|
|
str = "EDIT_USER_FAILED"
|
|
},
|
|
snmp_device_interface_status_change_failed = {
|
|
http_code = 400,
|
|
rc = -26,
|
|
str = "SNMP_DEVICE_INTERFACE_STATUS_CHANGE_FAILED"
|
|
},
|
|
configuration_file_mismatch = {
|
|
http_code = 400,
|
|
rc = -27,
|
|
str = "CONFIGURATION_FILE_MISMATCH"
|
|
},
|
|
partial_import = {http_code = 409, rc = -28, str = "PARTIAL_IMPORT"},
|
|
|
|
-- Infrastructure Dashboard
|
|
add_infrastructure_instance_failed = {
|
|
http_code = 409,
|
|
rc = -29,
|
|
str = "ADD_INFRASTRUCTURE_INSTANCE_FAILED"
|
|
},
|
|
edit_infrastructure_instance_failed = {
|
|
http_code = 409,
|
|
rc = -30,
|
|
str = "EDIT_INFRASTRUCTURE_INSTANCE_FAILED"
|
|
},
|
|
delete_infrastructure_instance_failed = {
|
|
http_code = 409,
|
|
rc = -31,
|
|
str = "DELETE_INFRASTRUCTURE_INSTANCE_FAILED"
|
|
},
|
|
infrastructure_instance_not_found = {
|
|
http_code = 404,
|
|
rc = -32,
|
|
str = "INFRASTRUCTURE_INSTANCE_NOT_FOUND"
|
|
},
|
|
|
|
infrastructure_instance_empty_id = {
|
|
http_code = 409,
|
|
rc = -33,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_ID"
|
|
},
|
|
infrastructure_instance_empty_alias = {
|
|
http_code = 409,
|
|
rc = -34,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_ALIAS"
|
|
},
|
|
infrastructure_instance_empty_url = {
|
|
http_code = 409,
|
|
rc = -35,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_URL"
|
|
},
|
|
infrastructure_instance_empty_token = {
|
|
http_code = 409,
|
|
rc = -36,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_TOKEN"
|
|
},
|
|
infrastructure_instance_empty_rtt_threshold = {
|
|
http_code = 409,
|
|
rc = -37,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_RTT_THRESHOLD"
|
|
},
|
|
|
|
infrastructure_instance_same_id = {
|
|
http_code = 409,
|
|
rc = -38,
|
|
str = "INFRASTRUCTURE_INSTANCE_SAME_ID"
|
|
},
|
|
infrastructure_instance_same_alias = {
|
|
http_code = 409,
|
|
rc = -39,
|
|
str = "INFRASTRUCTURE_INSTANCE_SAME_ALIAS"
|
|
},
|
|
infrastructure_instance_same_url = {
|
|
http_code = 409,
|
|
rc = -40,
|
|
str = "INFRASTRUCTURE_INSTANCE_SAME_URL"
|
|
},
|
|
infrastructure_instance_same_token = {
|
|
http_code = 409,
|
|
rc = -41,
|
|
str = "INFRASTRUCTURE_INSTANCE_SAME_TOKEN"
|
|
},
|
|
|
|
infrastructure_instance_already_existing = {
|
|
http_code = 409,
|
|
rc = -42,
|
|
str = "INFRASTRUCTURE_INSTANCE_ALREADY_EXISTING"
|
|
},
|
|
|
|
infrastructure_instance_check_failed = {
|
|
http_code = 409,
|
|
rc = -43,
|
|
str = "INFRASTRUCTURE_INSTANCE_CHECK_FAILED"
|
|
},
|
|
infrastructure_instance_check_not_found = {
|
|
http_code = 409,
|
|
rc = -44,
|
|
str = "INFRASTRUCTURE_INSTANCE_CHECK_NOT_FOUND"
|
|
},
|
|
infrastructure_instance_check_invalid_rsp = {
|
|
http_code = 409,
|
|
rc = -45,
|
|
str = "INFRASTRUCTURE_INSTANCE_CHECK_INVALID_RESPONSE"
|
|
},
|
|
infrastructure_instance_check_auth_failed = {
|
|
http_code = 409,
|
|
rc = -46,
|
|
str = "INFRASTRUCTURE_INSTANCE_CHECK_AUTH_FAILED"
|
|
},
|
|
infrastructure_instance_empty_bandwidth_threshold = {
|
|
http_code = 409,
|
|
rc = -47,
|
|
str = "INFRASTRUCTURE_INSTANCE_EMPTY_BANDWIDTH_THRESHOLD"
|
|
},
|
|
|
|
-- Widgets
|
|
widgets_missing_transformation = {
|
|
http_code = 409,
|
|
rc = -48,
|
|
str = "WIDGETS_MISSING_TRANSFORMATION"
|
|
},
|
|
widgets_missing_datasources = {
|
|
http_code = 409,
|
|
rc = -49,
|
|
str = "WIDGETS_MISSING_DATASOURCES"
|
|
},
|
|
widgets_missing_datasource_type = {
|
|
http_code = 409,
|
|
rc = -50,
|
|
str = "WIDGETS_MISSING_DATASOURCE_TYPE"
|
|
},
|
|
widgets_unknown_datasource_type = {
|
|
http_code = 409,
|
|
rc = -51,
|
|
str = "WIDGETS_UNKNOWN_DATASOURCE_TYPE"
|
|
},
|
|
widgets_missing_datasource_params = {
|
|
http_code = 409,
|
|
rc = -52,
|
|
str = "WIDGETS_MISSING_DATASOURCE_PARAMS"
|
|
},
|
|
|
|
add_pool_failed_too_many_pools = {
|
|
http_code = 409,
|
|
rc = -53,
|
|
str = "ADD_POOL_FAILED_TOO_MANY_POOLS"
|
|
},
|
|
add_pool_failed_too_many_pools_enterprise = {
|
|
http_code = 409,
|
|
rc = -54,
|
|
str = "ADD_POOL_FAILED_TOO_MANY_POOLS_ENTERPRISE"
|
|
},
|
|
|
|
-- nEdge
|
|
dhcp_active_leases_not_nedge = {
|
|
http_code = 409,
|
|
rc = -55,
|
|
str = "DHCP_ACTIVE_LEASES_NOT_NEDGE"
|
|
},
|
|
dhcp_active_leases_not_routing_mode = {
|
|
http_code = 409,
|
|
rc = -56,
|
|
str = "DHCP_ACTIVE_LEASES_NOT_ROUTING_MODE"
|
|
},
|
|
|
|
-- MAX_SNMP_DEVICES_NUM_REACHED
|
|
snmp_device_max_devices_num_reached = {
|
|
http_code = 400,
|
|
rc = -57,
|
|
str = "MAX_SNMP_DEVICES_NUM_REACHED"
|
|
},
|
|
|
|
-- ZMQ flag --encryption not present at startup
|
|
zmq_encryption_disabled = {
|
|
http_code = 503,
|
|
rc = -58,
|
|
str= "IFACE_ENCRYPTION_NOT_PRESENT"
|
|
},
|
|
|
|
-- Missing parameter(s) in request
|
|
missing_parameters = {
|
|
http_code = 422,
|
|
rc = -59,
|
|
str= "MISSING_PARAMETERS"
|
|
},
|
|
|
|
-- Checks
|
|
not_enabled = {http_code = 400, rc = -2, str = "NOT_ENABLED"}
|
|
}
|
|
}
|
|
}
|
|
|
|
-- ##############################################
|
|
|
|
function rest_utils.sendHTTPContentTypeHeader(content_type, content_disposition,
|
|
charset, extra_headers,
|
|
status_code)
|
|
local charset = charset or "utf-8"
|
|
local mime = content_type .. "; charset=" .. charset
|
|
|
|
rest_utils.sendHTTPHeader(mime, content_disposition, extra_headers,
|
|
status_code)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function rest_utils.sendHTTPHeaderIfName(mime, ifname, maxage,
|
|
content_disposition, extra_headers,
|
|
status_code, status_descr)
|
|
local info = ntop.getInfo(false)
|
|
local http_status_code_map = {
|
|
[200] = "OK",
|
|
[400] = "Bad Request",
|
|
[401] = "Unauthorized",
|
|
[403] = "Forbidden",
|
|
[404] = "Not Found",
|
|
[405] = "Method Not Allowed",
|
|
[406] = "Not Acceptable",
|
|
[408] = "Request timeout",
|
|
[409] = "Conflict",
|
|
[410] = "Gone",
|
|
[412] = "Precondition Failed",
|
|
[415] = "Unsupported Media Type",
|
|
[422] = "Unprocessable Content",
|
|
[423] = "Locked",
|
|
[428] = "Precondition Required",
|
|
[429] = "Too many requests",
|
|
[500] = "Internal Server Error",
|
|
[501] = "Not Implemented",
|
|
[503] = "Service Unavailable"
|
|
}
|
|
local tzname = info.tzname or ''
|
|
local cookie_attr = ntop.getCookieAttributes()
|
|
local lines = {
|
|
'Cache-Control: max-age=0, no-cache, no-store',
|
|
'Server: ntopng ' .. info["version"] .. ' [' .. info["platform"] .. ']',
|
|
'Set-Cookie: tzname=' .. tzname .. '; path=/' .. cookie_attr,
|
|
'Pragma: no-cache', 'X-Frame-Options: DENY',
|
|
'X-Content-Type-Options: nosniff', 'Content-Type: ' .. mime,
|
|
'Last-Modified: ' .. os.date("!%a, %m %B %Y %X %Z")
|
|
}
|
|
|
|
local uri = _SERVER.URI
|
|
|
|
if (starts(uri, "/lua/rest/")) then
|
|
--
|
|
-- Only for REST calls handle CORS (Cross-Origin Resource Sharing)
|
|
--
|
|
-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
|
-- https://web.dev/cross-origin-resource-sharing/
|
|
--
|
|
lines[#lines + 1] = 'Access-Control-Allow-Origin: *'
|
|
lines[#lines + 1] = 'Access-Control-Allow-Methods: GET, POST, HEAD'
|
|
end
|
|
|
|
if (_SESSION ~= nil) then
|
|
local key = "session_" .. info.http_port .. "_" .. info.https_port
|
|
lines[#lines + 1] =
|
|
'Set-Cookie: ' .. key .. '=' .. _SESSION["session"] .. '; max-age=' ..
|
|
maxage .. '; path=/; ' .. cookie_attr
|
|
end
|
|
|
|
if (ifname ~= nil) then
|
|
lines[#lines + 1] = 'Set-Cookie: ifname=' .. ifname .. '; path=/' ..
|
|
cookie_attr
|
|
end
|
|
|
|
if (info.timezone ~= nil) then
|
|
lines[#lines + 1] = 'Set-Cookie: timezone=' .. info.timezone ..
|
|
'; path=/' .. cookie_attr
|
|
end
|
|
|
|
if (content_disposition ~= nil) then
|
|
lines[#lines + 1] = 'Content-Disposition: ' .. content_disposition
|
|
end
|
|
|
|
if type(extra_headers) == "table" then
|
|
for hname, hval in pairs(extra_headers) do
|
|
lines[#lines + 1] = hname .. ': ' .. hval
|
|
end
|
|
end
|
|
|
|
if not status_code then status_code = 200 end
|
|
|
|
if not status_descr then status_descr = http_status_code_map[status_code] end
|
|
if not status_descr then status_descr = "Unknown" end
|
|
|
|
-- Buffer the HTTP reply and write it in one "print" to avoid fragmenting
|
|
-- it into multiple packets, to ease HTTP debugging with wireshark.
|
|
print("HTTP/1.1 " .. status_code .. " " .. status_descr .. "\r\n" ..
|
|
table.concat(lines, "\r\n") .. "\r\n\r\n")
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function rest_utils.sendHTTPHeaderLogout(mime, content_disposition)
|
|
rest_utils.sendHTTPHeaderIfName(mime, nil, 0, content_disposition)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function rest_utils.sendHTTPHeader(mime, content_disposition, extra_headers,
|
|
status_code, status_descr)
|
|
rest_utils.sendHTTPHeaderIfName(mime, nil, 3600, content_disposition,
|
|
extra_headers, status_code, status_descr)
|
|
end
|
|
|
|
-- Configure the module to return the REST answer locally
|
|
-- by setting a variable (rest_answer) rather than on HTTP
|
|
function rest_utils.enable_direct_mode()
|
|
rest_utils.direct_mode = true
|
|
rest_utils.rest_answer = nil
|
|
end
|
|
|
|
function rest_utils.disable_direct_mode()
|
|
rest_utils.direct_mode = false
|
|
rest_utils.rest_answer = nil
|
|
end
|
|
|
|
-- Return the REST answer locally (direct_mode)
|
|
function rest_utils.get_answer() return rest_utils.rest_answer end
|
|
|
|
function rest_utils.rc(ret_const, payload, additional_response_param, format)
|
|
local ret_code = ret_const.rc
|
|
local rc_str = ret_const.str -- String associated to the return code
|
|
local rc_str_hr -- String associated to the return code, human readable
|
|
|
|
-- Prepare the human readable string
|
|
rc_str_hr = i18n("rest_consts." .. rc_str) or "Unknown"
|
|
|
|
local client_rsp = {
|
|
rc = ret_code,
|
|
rc_str = rc_str,
|
|
rc_str_hr = rc_str_hr,
|
|
rsp = payload or {}
|
|
}
|
|
|
|
if additional_response_param ~= nil then
|
|
client_rsp = table.merge(additional_response_param, client_rsp)
|
|
end
|
|
|
|
if rest_utils.direct_mode then
|
|
rest_utils.rest_answer = client_rsp
|
|
return nil
|
|
elseif format and format == 'txt' then
|
|
return client_rsp
|
|
else
|
|
return json.encode(client_rsp)
|
|
end
|
|
end
|
|
|
|
function rest_utils.answer(ret_const, payload, extra_headers, status_descr)
|
|
if not rest_utils.direct_mode then
|
|
rest_utils.sendHTTPHeader('application/json', nil, extra_headers,
|
|
ret_const.http_code, status_descr)
|
|
end
|
|
|
|
local rsp = rest_utils.rc(ret_const, payload)
|
|
|
|
if rsp then print(rsp) end
|
|
end
|
|
|
|
function rest_utils.extended_answer(ret_const, payload,
|
|
additional_response_param, extra_headers,
|
|
format)
|
|
if not rest_utils.direct_mode then
|
|
local rsp_format = 'application/json'
|
|
if format and format == 'txt' then rsp_format = 'text/plain' end
|
|
rest_utils.sendHTTPHeader(rsp_format, nil, extra_headers,
|
|
ret_const.http_code)
|
|
end
|
|
|
|
local rsp = rest_utils.rc(ret_const, payload, additional_response_param,
|
|
format)
|
|
|
|
if rsp then print(rsp) end
|
|
end
|
|
|
|
function rest_utils.vanilla_payload_response(ret_const, payload, content_type,
|
|
extra_headers)
|
|
if content_type == nil then content_type = "text/plain" end
|
|
if (extra_headers == nil) then extra_headers = {} end
|
|
rest_utils.sendHTTPHeader(content_type, nil, extra_headers,
|
|
ret_const.http_code)
|
|
print(payload)
|
|
end
|
|
|
|
if (trace_script_duration ~= nil) then
|
|
io.write(debug.getinfo(1, 'S').source .. " executed in " ..
|
|
(os.clock() - clock_start) * 1000 .. " ms\n")
|
|
end
|
|
|
|
return rest_utils
|