ntopng/http_src/vue/modal-add-snmp-device.vue
2025-04-02 22:37:51 +02:00

611 lines
24 KiB
Vue

<!-- (C) 2023 - ntop.org -->
<template>
<modal ref="modal_id">
<template v-slot:title>{{ title }}</template>
<template v-slot:body>
<form>
<!-- Host -->
<template v-if="!is_edit_page">
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.snmp_host") }}</b>
</label>
<div class="col-sm-5 pe-0">
<input v-model="snmp_host" @input="check_host_regex" class="form-control"
:class="{ 'invalid': !is_host_correct }" type="text" :placeholder="host_placeholder"
required />
<small class="text-muted">{{ _i18n("snmp.descriptions.host") }}</small>
</div>
<div class="col-1 ps-5 pe-0 mt-1">
<span>/</span>
</div>
<div class="col-2 ps-0">
<input v-model="selected_cidr" @input="check_cidr" class="form-control" type="text"
:placeholder="cidr_placeholder" required />
</div>
</div>
</template>
<!-- SNMP Version -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.snmp_version") }}</b>
</label>
<div class="col-sm-5">
<SelectSearch v-model:selected_option="selected_version" :options="snmp_versions"
@select_option="update_v3_fields">
</SelectSearch>
<small class="text-muted">{{ _i18n("snmp.descriptions.version") }}</small>
</div>
</div>
<!-- SNMP Read Community -->
<div v-if="enable_v3_options == false" class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.snmp_read_community") }}</b>
</label>
<div class="col-sm-5">
<input v-model="snmp_read_community" class="form-control" type="text"
:class="{ 'invalid': !is_community_correct }" :placeholder="community_place_holder"
@input="check_community" required />
<small class="text-muted">{{ _i18n("snmp.descriptions.read_community") }}</small>
</div>
</div>
<!-- SNMP Write Community -->
<div v-if="props.context.snmp_set_available && enable_v3_options == false"
class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
{{ _i18n("snmp.snmp_write_community") }}
</label>
<div class="col-sm-5">
<input v-model="snmp_write_community" class="form-control" type="text" />
<small class="text-muted">{{ _i18n("snmp.descriptions.write_community") }}</small>
</div>
</div>
<!-- SNMP V3 -->
<template v-if="enable_v3_options">
<!-- SNMP Context -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.context") }}</b>
</label>
<div class="col-sm-5">
<input v-model="snmp_context" class="form-control" type="text" />
<small class="text-muted">{{ _i18n("snmp.descriptions.context")
}}</small>
</div>
</div>
<!-- SNMP Level -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.snmp_level") }}</b>
</label>
<div class="col-sm-5">
<SelectSearch v-model:selected_option="selected_snmp_level" :options="snmp_levels"
@select_option="update_levels">
</SelectSearch>
<small class="text-muted">{{ _i18n("snmp.descriptions.level") }}</small>
</div>
</div>
<template v-if="with_auth">
<!-- SNMP Username -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.snmp_username") }}</b>
</label>
<div class="col-sm-5">
<input v-model="snmp_username" class="form-control" type="text"
autocomplete="current-username" @input="check_username" required
:class="{ 'invalid': !is_username_valid }" />
<small class="text-muted">{{ _i18n("snmp.descriptions.username") }}</small>
</div>
</div>
<!-- SNMP Auth Protocol -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.authentication_protocol") }}</b>
</label>
<div class="col-sm-5">
<SelectSearch v-model:selected_option="selected_auth_protocol"
:options="snmp_auth_protocols">
</SelectSearch>
<small class="text-muted">{{ _i18n("snmp.descriptions.authentication_protocol")
}}</small>
</div>
</div>
<!-- SNMP Auth PassaPhrase -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.authentication_passphrase") }}</b>
</label>
<div class="col-sm-5">
<input v-model="snmp_auth_passphrase" class="form-control" type="password"
autocomplete="current-password" required @input="check_snmp_auth_passphrase"
:class="{ 'invalid': !is_snmp_auth_passphrase_valid }" />
<small class="text-muted">{{ _i18n("snmp.descriptions.authentication_passphrase")
}}</small>
</div>
</div>
</template>
<!-- SNMP Privacy Protocol -->
<template v-if="with_privacy">
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.privacy_protocol") }}</b>
</label>
<div class="col-sm-5">
<SelectSearch v-model:selected_option="selected_privacy_protocol"
:options="snmp_privacy_protocols">
</SelectSearch>
<small class="text-muted">{{ _i18n("snmp.descriptions.privacy_protocol") }}</small>
</div>
</div>
<!-- SNMP Privacy PassaPhrase -->
<div class="form-group ms-2 me-2 mt-3 row">
<label class="col-form-label col-sm-4">
<b>{{ _i18n("snmp.privacy_passphrase") }}</b>
</label>
<div class="col-sm-5">
<input v-model="snmp_privacy_passphrase" class="form-control"
autocomplete="current-password" type="password" required
@input="check_snmp_privacy_passphrase"
:class="{ 'invalid': !is_snmp_privacy_passphrase_valid }" />
<small class="text-muted">{{ _i18n("snmp.descriptions.privacy_passphrase") }}</small>
</div>
</div>
</template>
</template>
</form>
</template>
<template v-slot:footer>
<div v-if="is_data_not_ok" class="me-auto text-danger d-inline">
{{ no_host_feedback }}
</div>
<div>
<Spinner :show="activate_add_spinner" size="1rem" class="me-2"></Spinner>
<button v-if="is_edit_page == false" type="button" @click="add_" class="btn btn-primary"
:disabled="!(is_cidr_correct && is_host_correct && is_username_valid && is_snmp_auth_passphrase_valid && is_snmp_privacy_passphrase_valid && is_community_correct)">
{{ _i18n("add") }}
</button>
<button v-else type="button" @click="edit_" class="btn btn-primary"
:disabled="!(is_cidr_correct && is_host_correct && is_username_valid && is_snmp_auth_passphrase_valid && is_snmp_privacy_passphrase_valid && is_community_correct)">
{{ _i18n("apply") }}
</button>
</div>
</template>
</modal>
</template>
<script setup>
/* Imports */
import { ref, } from "vue";
import { default as modal } from "./modal.vue";
import { default as SelectSearch } from "./select-search.vue";
import { default as NoteList } from "./note-list.vue";
import { default as Spinner } from "./spinner.vue";
import { default as dataUtils } from "../utilities/data-utils";
import regexValidation from "../utilities/regex-validation.js";
import NtopUtils from "../utilities/ntop-utils";
/* ****************************************************** */
const _i18n = (t) => i18n(t);
const emit = defineEmits(["add", "edit"]);
const props = defineProps({
context: Object,
});
/* Consts */
const title = ref(_i18n("snmp.add_snmp_devices"));
const host_placeholder = ref(_i18n("snmp.device_example").replace("%{example}", "192.168.1.2"));
const community_place_holder = ref(_i18n("snmp.device_example").replace("%{example}", "public"));
const cidr_placeholder = i18n("hosts_stats.page_scan_hosts.cidr_placeholder");
const modal_id = ref(null);
const row_to_edit_id = ref("");
const activate_add_spinner = ref(false);
const is_edit_page = ref(false);
const ifid = ref(null);
const snmp_host = ref(null);
const snmp_read_community = ref(null);
const snmp_context = ref(null);
const snmp_write_community = ref(null);
const is_enterprise_l = ref(null);
const is_cidr_correct = ref(false);
const is_host_correct = ref(false);
const is_community_correct = ref(false);
const is_snmp_auth_passphrase_valid = ref(true);
const is_snmp_privacy_passphrase_valid = ref(true);
const is_username_valid = ref(true);
const no_host_feedback = ref("");
const with_privacy = ref(false);
const with_auth = ref(false);
const enable_v3_options = ref(false);
const selected_version = ref(null);
const snmp_versions = props.context.snmp_v3_available ?
ref([
{ id: "0", label: "v1" },
{ id: "1", label: "v2c" },
{ id: "2", label: "v3" },
]) :
ref([
{ id: "0", label: "v1" },
{ id: "1", label: "v2c" }]);
const selected_snmp_level = ref(null);
const snmp_levels = ref([
{ id: "authNoPriv", label: _i18n("snmp.snmp_authnopriv") },
{ id: "authPriv", label: _i18n("snmp.snmp_authpriv") },
{ id: "noAuthNoPriv", label: _i18n("snmp.snmp_noauthnopriv") }
]);
const selected_auth_protocol = ref(null);
const snmp_auth_protocols = ref([
{ id: "md5", label: "MD5" },
{ id: "sha", label: "SHA" },
{ id: "sha256", label: "SHA256" },
{ id: "sha384", label: "SHA384" },
{ id: "sha512", label: "SHA512" },
]);
const snmp_privacy_protocols = ref([
{ id: "des", label: "DES" },
{ id: "aes", label: "AES" },
{ id: "aes128", label: "AES128" },
]);
const selected_privacy_protocol = ref(null);
const snmp_username = ref(null);
const snmp_auth_passphrase = ref(null);
const snmp_privacy_passphrase = ref("");
const CIDR_24 = 24;
const CIDR_32 = 32;
const CIDR_128 = 128;
const selected_cidr = ref(null);
const is_data_not_ok = ref(false);
const refresh_select_search = ref(false);
/* ****************************************************** */
/*
* Reset fields in modal form
*/
const reset_modal_form = function () {
snmp_host.value = "";
selected_cidr.value = "";
snmp_read_community.value = "";
snmp_context.value = "";
snmp_write_community.value = "";
no_host_feedback.value = "";
snmp_auth_passphrase.value = "";
snmp_username.value = "";
selected_snmp_level.value = snmp_levels.value[2];
selected_auth_protocol.value = snmp_auth_protocols.value[0];
selected_privacy_protocol.value = snmp_privacy_protocols.value[0];
snmp_privacy_passphrase.value = "";
is_cidr_correct.value = false;
is_host_correct.value = false;
is_community_correct.value = false;
activate_add_spinner.value = false;
row_to_edit_id.value = null;
is_edit_page.value = false;
is_data_not_ok.value = false;
selected_version.value = snmp_versions.value[0];
enable_v3_options.value = false;
is_snmp_auth_passphrase_valid.value = true;
is_snmp_privacy_passphrase_valid.value = true;
is_username_valid.value = true;
};
/* ****************************************************** */
/*
* Set row to edit
*/
const set_row_to_edit = (row) => {
is_edit_page.value = true;
/* Set host values */
snmp_host.value = row.ip;
selected_cidr.value = row.cidr;
is_host_correct.value = true;
is_cidr_correct.value = true;
is_community_correct.value = true;
is_username_valid.value = true;
selected_version.value = snmp_versions.value.find((item) => item.label == row.column_version);
if (selected_version.value.id != '2') {
snmp_read_community.value = row.column_community;
snmp_write_community.value = row.column_write_community;
} else {
enable_v3_options.value = true;
snmp_context.value = row.column_context
snmp_auth_passphrase.value = row.column_auth_passphrase;
selected_auth_protocol.value = snmp_auth_protocols.value.find((item) => item.id == row.column_auth_protocol);
selected_snmp_level.value = snmp_levels.value.find((item) => item.id == row.column_level);
selected_privacy_protocol.value = snmp_privacy_protocols.value.find((item) => item.id == row.column_privacy_protocol);
snmp_privacy_passphrase.value = row.column_privacy_passphrase;
snmp_username.value = row.column_username;
switch (selected_snmp_level.value.id) {
case "authNoPriv": {
with_privacy.value = false;
with_auth.value = true;
} break;
case "authPriv": {
with_privacy.value = true;
with_auth.value = true;
} break;
case "noAuthNoPriv": {
with_privacy.value = false;
with_auth.value = false;
}
}
}
};
/* ****************************************************** */
/* This method is called whenever the modal is opened */
const show = (row, _host) => {
/* First of all reset all the data */
reset_modal_form();
title.value = i18n("snmp.add_snmp_devices");
if (!dataUtils.isEmptyOrNull(row)) {
/* In case row is not null then an edit is requested */
const device_name = row.column_name != null && row.column_name != "" ? row.column_name : row.ip;
title.value = `${_i18n("snmp.edit_snmp_devices")}: ${device_name}`;
set_row_to_edit(row);
}
modal_id.value.show();
};
const update_levels = () => {
is_community_correct.value = true;
switch (selected_snmp_level.value.id) {
case "authNoPriv": {
with_privacy.value = false;
with_auth.value = true;
is_snmp_auth_passphrase_valid.value = false;
is_snmp_privacy_passphrase_valid.value = true;
is_username_valid.value = false;
snmp_auth_passphrase.value = "";
snmp_privacy_passphrase.value = "";
} break;
case "authPriv": {
with_privacy.value = true;
with_auth.value = true;
is_snmp_auth_passphrase_valid.value = false;
is_snmp_privacy_passphrase_valid.value = false;
is_username_valid.value = false;
snmp_auth_passphrase.value = "";
snmp_privacy_passphrase.value = "";
} break;
case "noAuthNoPriv": {
with_privacy.value = false;
with_auth.value = false;
is_snmp_auth_passphrase_valid.value = true;
is_snmp_privacy_passphrase_valid.value = true;
is_username_valid.value = true;
snmp_auth_passphrase.value = "";
snmp_privacy_passphrase.value = "";
} break;
}
}
const update_v3_fields = () => {
if (selected_version.value.id == "2" && props.context.snmp_v3_available) {
enable_v3_options.value = true;
is_community_correct.value = true;
} else {
enable_v3_options.value = false;
}
}
/* ****************************************************** */
/* Regex to check if the host is correct or not */
const check_host_regex = () => {
const is_ipv4 = regexValidation.validateIPv4(snmp_host.value);
const is_ipv6 = regexValidation.validateIPv6(snmp_host.value);
const is_host_name = regexValidation.validateHostName(snmp_host.value);
/* When it isn't the ipv4_netscan case the cidr selection is enabled */
if (is_ipv4) {
/* IPv4 */
is_host_correct.value = true;
selected_cidr.value = CIDR_32;
is_cidr_correct.value = true;
} else if (is_ipv6) {
/* IPv6 */
is_host_correct.value = true;
selected_cidr.value = CIDR_128;
is_cidr_correct.value = true;
} else if (is_host_name) {
/* Host Name */
is_host_correct.value = true;
} else {
is_host_correct.value = false;
}
};
const check_community = () => {
if (snmp_read_community.value) {
is_community_correct.value = true;
} else {
is_community_correct.value = false;
}
}
const check_cidr = () => {
if (selected_cidr.value == CIDR_24 || selected_cidr.value == CIDR_32 || selected_cidr.value == CIDR_128) {
is_cidr_correct.value = true;
return true;
}
is_cidr_correct.value = false;
return false;
}
const check_username = () => {
if (snmp_username.value != null && snmp_username.value != "") {
is_username_valid.value = true;
} else {
is_username_valid.value = false;
}
}
const check_snmp_auth_passphrase = () => {
if (snmp_auth_passphrase.value != null && snmp_auth_passphrase.value.length >= 6) {
is_snmp_auth_passphrase_valid.value = true;
} else {
is_snmp_auth_passphrase_valid.value = false;
}
}
const check_snmp_privacy_passphrase = () => {
if (snmp_privacy_passphrase.value != null && snmp_privacy_passphrase.value.length >= 6) {
is_snmp_privacy_passphrase_valid.value = true;
} else {
is_snmp_privacy_passphrase_valid.value = false;
}
}
/* ****************************************************** */
/* Resolve hostname */
async function resolve_host_name(host) {
const resolve_host_name_url = `${http_prefix}/lua/rest/v2/get/host/resolve_host_name.lua`;
const url = NtopUtils.buildURL(resolve_host_name_url, {
host: host,
});
return await ntopng_utility.http_request(url);
}
/* ****************************************************** */
/* Function called when the edit button is clicked */
const edit_ = () => {
add_(true);
};
/* ****************************************************** */
/* Function called when the modal is closed */
const close = () => {
modal_id.value.close();
};
/* ****************************************************** */
/* Function to add host to scan */
const add_ = async (is_edit) => {
const emit_event = (is_edit === true) ? "edit" : "add";
/* Activate the spinner to give the user a feedback */
activate_add_spinner.value = true;
/* Check if it's an IP or not, if not it means it's an hostname */
let new_host_name_resolved = true;
let new_host = snmp_host.value;
if (!regexValidation.validateIP(snmp_host.value)) {
/* During the validation disable the add button */
is_host_correct.value = false;
new_host = await resolve_host_name(snmp_host.value);
if (new_host === "no_success") {
/* The resolution failed! */
new_host_name_resolved = false;
no_host_feedback.value = snmp_host.value + " " + i18n("hosts_stats.page_scan_hosts.host_not_resolved");
is_data_not_ok.value = true;
/* Hide the message after 3 seconds */
setTimeout(() => {
is_data_not_ok.value = false
}, 4000)
}
/* Validation ended, re-enable the button */
is_host_correct.value = true;
}
/* If the resolution was ok or no resolution at all was done emit the event */
activate_add_spinner.value = new_host_name_resolved;
if (new_host_name_resolved) {
/* Emit the event, only if the resolution
was ok or no resolution at all was needed */
let snmp_entry = {
snmp_host: new_host,
cidr: selected_cidr.value,
snmp_version: selected_version.value.id,
snmp_read_community: snmp_read_community.value,
snmp_write_community: snmp_write_community.value
}
if (selected_version.value.id == '2') {
let tmp_selected_auth_protocol = selected_auth_protocol.value != null ? selected_auth_protocol.value.id : snmp_auth_protocols.value[0].id;
let tmp_selected_snmp_level = selected_snmp_level.value != null ? selected_snmp_level.value.id : snmp_levels.value[0].id;
let tmp_selected_privacy_protocol = selected_privacy_protocol.value != null ? selected_privacy_protocol.value.id : snmp_privacy_protocols.value[0].id;
snmp_entry = {
...snmp_entry, ...{
snmp_auth_passphrase: snmp_auth_passphrase.value,
snmp_auth_protocol: tmp_selected_auth_protocol,
snmp_level: tmp_selected_snmp_level,
snmp_privacy_protocol: tmp_selected_privacy_protocol,
snmp_privacy_passphrase: snmp_privacy_passphrase.value,
snmp_username: snmp_username.value,
snmp_context: snmp_context.value
}
}
}
is_data_not_ok.value = false;
emit(emit_event,
snmp_entry
);
}
};
/* ****************************************************** */
/* Load the available metrics */
const metricsLoaded = async (_scan_type_list, _ifid, _is_enterprise_l) => {
const scan_types = _scan_type_list.sort((a, b) =>
a.label.localeCompare(b.label)
);
ifid.value = _ifid;
scan_type_list.value = scan_types;
discovered_scan_type_list.value = scan_types.filter((item) => (item.id != 'ipv4_netscan'));
is_enterprise_l.value = _is_enterprise_l;
selected_scan_type.value = scan_type_list.value[0];
};
const show_bad_feedback = (feedback_msg) => {
is_data_not_ok.value = true;
no_host_feedback.value = feedback_msg;
activate_add_spinner.value = false;
}
defineExpose({ show, close, metricsLoaded, show_bad_feedback });
</script>