Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,26 @@

<script setup lang="ts">
import {
focusElement,
getAxiosErrorMessage,
NeButton,
NeCombobox,
type NeComboboxOption,
NeTooltip,
NeExpandable,
NeInlineNotification,
NeSideDrawer,
NeButton,
NeRadioSelection,
focusElement,
NeSideDrawer,
NeSkeleton,
NeTextInput,
getAxiosErrorMessage
NeTooltip
} from '@nethesis/vue-components'
import { ref, computed, type PropType, type Ref, watch, nextTick } from 'vue'
import { computed, nextTick, type PropType, type Ref, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import {
useFirewallStore,
type NatRule,
type NatRuleAction,
type RuleHost
type RuleHost,
useFirewallStore
} from '@/stores/standalone/firewall'
import {
MessageBag,
Expand All @@ -33,7 +35,8 @@ import {
validateRequired,
type validationOutput
} from '@/lib/validation'
import { ValidationError, ubusCall } from '@/lib/standalone/ubus'
import { ubusCall, ValidationError } from '@/lib/standalone/ubus'
import type { AxiosResponse } from 'axios'

const props = defineProps({
currentRule: {
Expand All @@ -59,6 +62,11 @@ const rewriteIpAddress = ref('')
const rewriteIpAddressRef = ref()
const sourceAddressOptions = ref<NeComboboxOption[]>([])
const destinationAddressOptions = ref<NeComboboxOption[]>([])
const advancedSettingsExpanded = ref(false)
const device = ref('')
const availableDevices = ref<NeComboboxOption[]>([])
const loadingAvailableDevices = ref(false)
const errorAvailableDevices = ref<Error>()
const errorBag = ref(new MessageBag())
// contains the first invalid field ref
const firstErrorRef = ref()
Expand Down Expand Up @@ -118,6 +126,16 @@ const zoneOptions = computed(() => {
return [anyAddress, ...zones]
})

const deviceOptions = computed<NeComboboxOption[]>(() => {
return [
{
id: '',
label: t('common.any')
},
...availableDevices.value
]
})

watch(
() => props.isShown,
() => {
Expand All @@ -130,22 +148,27 @@ watch(
})
firewallConfig.fetch()
listHostSuggestions()
listAvailableDevices()
sourceAddress.value = ''
destinationAddress.value = ''
advancedSettingsExpanded.value = false
Comment thread
Tbaile marked this conversation as resolved.

if (isCreatingRule.value) {
// creating rule, reset form to defaults
ruleName.value = ''
outboundZone.value = '*'
action.value = 'SNAT'
rewriteIpAddress.value = ''
device.value = ''
} else if (props.currentRule) {
// editing rule
ruleName.value = props.currentRule.name || ''
// source/destination addresses will be set inside listHostSuggestions
outboundZone.value = props.currentRule.src || '*'
action.value = props.currentRule.target || 'SNAT'
rewriteIpAddress.value = props.currentRule.snat_ip || ''
device.value = props.currentRule.device || ''
advancedSettingsExpanded.value = props.currentRule.device != ''
}
}
}
Expand All @@ -160,6 +183,23 @@ function clearErrors() {
error.value.listHostSuggestionsDetails = ''
error.value.saveRule = ''
error.value.saveRuleDetails = ''
errorAvailableDevices.value = undefined
}

type ListDevicesResponse = AxiosResponse<{
devices: {
id: string
label: string
}[]
}>

function listAvailableDevices() {
loadingAvailableDevices.value = true
errorAvailableDevices.value = undefined
ubusCall('ns.nat', 'list-devices')
.then((response: ListDevicesResponse) => (availableDevices.value = response.data.devices))
.catch((reason) => (errorAvailableDevices.value = reason))
.finally(() => (loadingAvailableDevices.value = false))
}

async function listHostSuggestions() {
Expand Down Expand Up @@ -318,7 +358,8 @@ async function saveRule() {
src_ip: sourceAddress.value,
dest_ip: destinationAddress.value,
target: action.value,
snat_ip: action.value === 'SNAT' ? rewriteIpAddress.value : ''
snat_ip: action.value === 'SNAT' ? rewriteIpAddress.value : '',
device: device.value
}

if (isEditingRule.value) {
Expand Down Expand Up @@ -456,6 +497,33 @@ async function saveRule() {
:invalid-message="t(errorBag.getFirstI18nKeyFor('snat_ip'))"
:disabled="loading.saveRule"
/>
<NeInlineNotification
v-if="errorAvailableDevices"
:description="errorAvailableDevices.message"
:title="t(getAxiosErrorMessage(errorAvailableDevices))"
kind="error"
/>
<NeExpandable
:label="t('common.advanced_settings')"
:is-expanded="advancedSettingsExpanded"
@set-expanded="(ev: boolean) => (advancedSettingsExpanded = ev)"
>
<NeSkeleton v-if="loadingAvailableDevices" :lines="2" />
<NeCombobox
v-else-if="errorAvailableDevices == undefined"
v-model="device"
:disabled="loading.saveRule"
:label="t('standalone.nat.device')"
:options="deviceOptions"
:invalid-message="t(errorBag.getFirstI18nKeyFor('device'))"
:no-results-label="t('ne_combobox.no_results')"
:limited-options-label="t('ne_combobox.limited_options_label')"
:no-options-label="t('ne_combobox.no_options_label')"
:selected-label="t('ne_combobox.selected')"
:user-input-label="t('ne_combobox.user_input_label')"
:optional-label="t('common.optional')"
/>
</NeExpandable>
<!-- saveRule error notification -->
<NeInlineNotification
v-if="error.saveRule"
Expand Down
15 changes: 10 additions & 5 deletions src/components/standalone/firewall/nat/NatRulesTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { NeButton } from '@nethesis/vue-components'
import {
NeBadge,
NeButton,
NeDropdown,
NePaginator,
NeTable,
NeTableBody,
NeTableCell,
NeTableHead,
NeTableHeadCell,
NeTableBody,
NeTableRow,
NeTableCell,
NePaginator,
useItemPagination
} from '@nethesis/vue-components'
import { ref, type PropType } from 'vue'
import { type PropType, ref } from 'vue'
import { type NatRule } from '@/stores/standalone/firewall'
import { getZoneColorClasses } from '@/lib/standalone/network'
import { faTrash } from '@fortawesome/free-solid-svg-icons'
Expand Down Expand Up @@ -71,6 +71,7 @@ function getDropdownItems(rule: NatRule) {
<NeTableHeadCell>{{ t('standalone.nat.destination_address') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.nat.action') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.nat.rewrite_ip') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.nat.device') }}</NeTableHeadCell>
<NeTableHeadCell>
<!-- no header for actions -->
</NeTableHeadCell>
Expand Down Expand Up @@ -102,6 +103,10 @@ function getDropdownItems(rule: NatRule) {
<span v-if="item.snat_ip">{{ item.snat_ip }}</span>
<span v-else> - </span>
</NeTableCell>
<NeTableCell :data-label="t('standalone.nat.device')">
<span v-if="item.device">{{ item.device }}</span>
<span v-else>{{ t('common.any') }}</span>
</NeTableCell>
<NeTableCell :data-label="t('common.actions')">
<div class="align-center -ml-2.5 flex items-center gap-2 xl:ml-0 xl:justify-end">
<NeButton kind="tertiary" size="lg" @click="emit('editRule', item)">
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2292,7 +2292,8 @@
"delete_nat_rule": "Delete NAT rule",
"confirm_delete_rule": "You are about to delete NAT rule '{name}'",
"any_address": "Any address",
"rules_and_netmap": "Rules and NETMAP"
"rules_and_netmap": "Rules and NETMAP",
"device": "Device"
},
"netmap": {
"title": "NETMAP",
Expand Down
1 change: 1 addition & 0 deletions src/stores/standalone/firewall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface NatRule {
dest_ip: string
target: NatRuleAction
snat_ip?: string
device: string
Comment thread
Tbaile marked this conversation as resolved.
}

export interface NetmapRule {
Expand Down
Loading