Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created network configuration page #8692

Merged
merged 8 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions http_src/vue/ntop_vue.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { default as PageProbes } from "./page-probes.vue"
import { default as PageExporters } from "./page-exporters.vue"
import { default as PageExportersDetails } from "./page-exporters-details.vue"
import { default as PageExportersInterfaces } from "./page-exporters-interfaces.vue"
import { default as PageNetworkConfiguration } from "./page-network-configuration.vue"

/* Config pages */
import { default as PageSNMPConfig } from "./page-snmp-config.vue"
Expand Down Expand Up @@ -190,6 +191,7 @@ let ntopVue = {
PageFlowDeviceInterfaceDetails: PageFlowDeviceInterfaceDetails,
PageHistoricalFlow: PageHistoricalFlow,
PageExportersInterfaces: PageExportersInterfaces,
PageNetworkConfiguration: PageNetworkConfiguration,
//PageSankeyTest: PageSankeyTest,

// components
Expand Down
153 changes: 153 additions & 0 deletions http_src/vue/page-network-configuration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<template>
<div class="container mt-4">
<div v-for="(value, key) in check_name" :key="key" class="mb-4">
<div class="mb-2">
<strong>{{ _i18n(value.i18n_title) }}</strong>
</div>
<div>
<textarea v-model="ipAddresses[key]" class="form-control rounded"
:placeholder="`Enter ${value.device_type} IPs (Comma Separated)`" @input="markAsModified(key)"
rows="3"></textarea>
<div v-if="validationErrors[key]" class="text-danger mt-1">
{{ validationErrors[key] }}
</div>
</div>
</div>
<div class="d-flex justify-content-end mt-4">
<button @click="saveConfig" :class="saveButtonClass" :disabled="isSaving"> {{ saveButtonText }}
</button>
</div>
</div>
</template>

<script setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { ntopng_utility } from "../services/context/ntopng_globals_services.js";
import regexValidation from "../utilities/regex-validation.js";

const _i18n = (t) => i18n(t);

const props = defineProps({
context: Object
});


const ipAddresses = reactive({});
const validationErrors = reactive({});
const set_config_url = `${http_prefix}/lua/rest/v2/set/network/config.lua`
const get_config_url = `${http_prefix}/lua/rest/v2/get/network/config.lua?ifid=${props.context.ifid}`
const modifiedInputs = ref([]);

const isSaving = ref(false);
const saveSuccess = ref(false);

const saveButtonText = computed(() => {
if (isSaving.value) return 'Saving...';
if (saveSuccess.value) return 'Saved!';
return _i18n("flow_checks.save_configuration");
});

const saveButtonClass = computed(() => {
if (saveSuccess.value) return 'btn btn-success';
return 'btn btn-primary';
});

const check_name = {
"dns_list": { "i18n_title": "flow_checks.dns_servers_title", "device_type": "DNS Server", "reques_param": "dns_list" },
"ntp_list": { "i18n_title": "flow_checks.ntp_servers_title", "device_type": "NTP Server", "reques_param": "ntp_list" },
"dhcp_list": { "i18n_title": "flow_checks.dhcp_servers_title", "device_type": "DHCP Server", "reques_param": "dhcp_list" },
"smtp_list": { "i18n_title": "flow_checks.smtp_servers_title", "device_type": "SMTP Server", "reques_param": "smtp_list" },
"gateway": { "i18n_title": "flow_checks.gateway", "device_type": "Gateway", "reques_param": "gateway" },
}

Object.keys(check_name).forEach(key => {
ipAddresses[key] = '';
});

onMounted(() => {
getConfig();
});

// Function used to populate text area with data received from the backend at page initialization
const getConfig = async () => {
const data = await ntopng_utility.http_request(get_config_url)

data.forEach(item => {
const key = Object.keys(check_name).find(k => k === item.key);
if (key && item.is_enabled === true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary

ipAddresses[key] = Array.isArray(item.value_description)
? item.value_description.join(', ')
: item.value_description;
}
})
};

// Used to mark a text area as modified so that only modified text areas are sent to the backend to be stored in redis
const markAsModified = (key) => {
if (!modifiedInputs.value.includes(key)) {
modifiedInputs.value.push(key);
}
};

// Function to validate IP addresses inserted in text area
const validateIpAddresses = () => {
let isValid = true;
Object.keys(ipAddresses).forEach(key => {
const ips = ipAddresses[key].split(',').map(ip => ip.trim()).filter(ip => ip !== '');
if (ips.length === 0) {
validationErrors[key] = '';
} else if (!ips.every(regexValidation.validateIP)) {
validationErrors[key] = 'Invalid IP address format';
isValid = false;
} else {
validationErrors[key] = '';
}
});
return isValid;
};

// Function used to post data to the backend and save the values in
const saveConfig = async () => {
if (validateIpAddresses()) {
isSaving.value = true;
let data = { csrf: props.context.csrf, config: []};

let headers = {
'Content-Type': 'application/json'
};

try {
for (const key of modifiedInputs.value) {
const value = ipAddresses[key];
const ips = value.split(',').map(ip => ip.trim());

let requestData = {
asset_key: check_name[key].reques_param,
item: ips
};

data.config.push(requestData)

}

console.log(data)
//await ntopng_utility.http_post_request(set_config_url, data);

await ntopng_utility.http_request(set_config_url, { method: 'post', headers, body: JSON.stringify(data) })
modifiedInputs.value = [];

// Show success when saved
saveSuccess.value = true;
setTimeout(() => {
saveSuccess.value = false;
}, 1500);

} catch (error) {
console.error('Save failed:', error);

} finally {
isSaving.value = false;
}
}
};
</script>
7 changes: 7 additions & 0 deletions scripts/locales/en.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1651,6 +1651,7 @@ local lang = {
},
},
["checks"] = {
["network_configuration"] = "Network Configuration",
["categories"] = "Categories",
["category"] = "Category",
["category_active_monitoring"] = "Active Monitoring",
Expand Down Expand Up @@ -2848,6 +2849,12 @@ local lang = {
["visual_explorer"] = "Visual Explorer",
},
["flow_checks"] = {
["dns_servers_title"] = "DNS Servers List",
["ntp_servers_title"] = "NTP Servers List",
["smtp_servers_title"] = "SMTP Servers List",
["dhcp_servers_title"] = "DHCP Servers List",
["save_configuration"] = "Save Configuration",
["gateway"] = "Gateway",
["allowed_server_names_description"] = "Comma separated values of allowed server IPs. Example: 173.194.76.109,my.example.com",
["allowed_servers_description"] = "Comma separated values of allowed server IPs. Example: 173.194.76.109,52.97.232.242",
["allowed_servers_title"] = "Allowed Servers",
Expand Down
44 changes: 44 additions & 0 deletions scripts/lua/admin/network_configuration.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
--
-- (C) 2020 - ntop.org
--
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

require "lua_utils"
require "ntop_utils"

local page_utils = require "page_utils"
local json = require "dkjson"
local template_utils = require("template_utils")
local ifid = interface.getId()

sendHTTPContentTypeHeader('text/html')

page_utils.print_header_and_set_active_menu_entry(page_utils.menu_entries.network_config)

dofile(dirs.installdir .. "/scripts/lua/inc/menu.lua")

local ifstats = interface.getStats()
local probes = ifstats.probes

page_utils.print_navbar(i18n("checks.network_configuration"), ntop.getHttpPrefix() .. "/lua/admin/network_configuration.lua", {{
url = ntop.getHttpPrefix() .. "/lua/admin/network_configuration.lua",
active = true,
page_name = "assets_inventory",
label = "<i class=\"fas fa-lg fa-home\" data-bs-toggle=\"tooltip\" data-bs-placement=\"top\" title=\"" ..
i18n("checks.network_configuration") .. "\"></i>"
}})

local context = {
ifid = interface.getId(),
csrf = ntop.getRandomCSRFValue()
}

local json_context = json.encode(context)

template_utils.render("pages/vue_page.template", {
vue_page_name = "PageNetworkConfiguration",
page_context = json_context
})

dofile(dirs.installdir .. "/scripts/lua/inc/footer.lua")
10 changes: 5 additions & 5 deletions scripts/lua/modules/http_lint.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1968,14 +1968,17 @@ local known_parameters = {
["flow_hash_id"] = validateNumber, -- The ID uniquely identifying the flow in the hash table
["user"] = validateSingleWord, -- The user ID
["snapshot_name"] = validateSingleWord, -- The user ID
["command"] = validateSingleWord, -- The user ID
["report_name"] = validateSingleWord, -- The report name
["report_template"] = validateSingleWord, -- The report template
["report_title"] = validateSingleWord, -- The report title (used in vs reports)
["pool"] = validateNumber, -- A pool ID
["pool_id"] = validateNumber, -- A pool ID
["direction"] = validateDirection, -- Sent or Received direction
["download"] = validateBool,
["item"] = validateSingleWord, -- Used by the Import/Export page to select the item to import/export
["item"] = validateJSON, -- Used by the Import/Export page to select the item to import/export
["config"] = validateUnquoted, -- Used by the Import/Export page to select the item to import/export
["asset_key"] = validateSingleWord, -- Used by network configuration
["stats_type"] = validateStatsType, -- A mode for historical stats queries
["alertstats_type"] = validateAlertStatsType, -- A mode for alerts stats queries
["flowhosts_type"] = validateFlowHostsTypeOrIpVersionOrIp, -- A filter for local/remote hosts in each of the two directions
Expand Down Expand Up @@ -2827,7 +2830,6 @@ local function validateParameter(k, v)
if (type(v) == "table") then
for table_key, table_value in pairs(v) do
local success, message, purified = validateParameter(table_key, table_value)

-- Stop, if any of the table value fails the validation against
-- the expected table key
if not success then
Expand Down Expand Up @@ -2925,10 +2927,9 @@ local function lintParams()

-- Extend the parameters with validators from the scripts
local additional_params = script_manager.extendLintParams(http_lint, known_parameters)

for _, id in pairs(params_to_validate) do
for k, v in pairs(id) do
if (debug) then
if (false) then
io.write("[LINT] Validating [" .. k .. "]\n")
end

Expand All @@ -2939,7 +2940,6 @@ local function lintParams()
end
else
local success, message, purified = validateSpecialParameter(k, v)

if success then
id[k] = purified
else
Expand Down
1 change: 1 addition & 0 deletions scripts/lua/modules/page_utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ page_utils.menu_entries = {
nedge_users = {key = "nedge_users", i18n_title = "manage_users.manage_users", section = "admin", help_link = "https://www.ntop.org/guides/nedge/users.html#"},
manage_users = {key = "manage_users", i18n_title = ternary(is_nedge, "nedge.system_users", "manage_users.manage_users"), section = "admin", help_link = "https://www.ntop.org/guides/ntopng/web_gui/settings.html#manage-users"},
preferences = {key = "preferences", i18n_title = "prefs.preferences", section = "admin", help_link = "https://www.ntop.org/guides/ntopng/web_gui/settings.html#preferences"},
network_config = {key = "network_config", i18n_title = "checks.network_configuration", section = "admin"},
scripts_config = {key = "scripts_config", i18n_title = "about.checks", section = "admin", help_link = "https://www.ntop.org/guides/ntopng/web_gui/checks.html"},
scripts_config_all = {key = "scripts_config", subkey="all", i18n_title = "all", section = "admin", help_link = "https://www.ntop.org/guides/ntopng/web_gui/checks.html"},
scripts_config_hosts = {key = "scripts_config", subkey="hosts", i18n_title = alert_entities.host.i18n_label, section = "admin", help_link = "https://www.ntop.org/guides/ntopng/web_gui/checks.html"},
Expand Down
27 changes: 27 additions & 0 deletions scripts/lua/rest/v2/get/network/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

--
-- (C) 2021 - ntop.org
--

local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

require "lua_utils"
local rest_utils = require "rest_utils"
local json = require "dkjson"

local ifid = tonumber(_GET["ifid"])


local res = {}

-- Get data from redis: expected format, array of objects with keys:
res = {{key= "unexpected_dhcp", value_description="192.168.2.85, 192.168.2.45" or "", is_enabled=true or false}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_enabled is not necessary



if isEmptyString(ifid) then
rest_utils.answer(rest_utils.consts.err.invalid_interface)
return
end

rest_utils.answer(rest_utils.consts.success.ok, res)
41 changes: 41 additions & 0 deletions scripts/lua/rest/v2/set/network/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

--
-- (C) 2021 - ntop.org
--

local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path

require "lua_utils"
local rest_utils = require "rest_utils"
local json = require "dkjson"

local action = _GET["action"]
local post_data = _POST["payload"]

local res = {}

local config = _POST["config"]
tprint(_POST)

local data = json.decode(config)

-- data is:
--[[
asset_key = [ "gateway", "unexpected_dhcp", "unexpected_dns", "unexpected_ntp", "unexpected_smtp"]

{ "csrf":..., "config": [ {"asset_key": asset_key, "item": [ip1, ip2, ip3...]}, {"asset_key": asset_key_1, "item": [ip1, ip2, ip3...]}]}
]]

-- local script_key = post_data["asset_key"] -- asset_key
-- local redis_key = "ntopng.prefs." .. script_key .. "_ip_list"

-- for each element in respone: ntop.getCache(redis_key)

if isEmptyString(ifid) then
rest_utils.answer(rest_utils.consts.err.invalid_interface)
return
end


rest_utils.answer(rest_utils.consts.success.ok, res)
Loading