Skip to content

Commit

Permalink
feat(host set): add validation for IP range compatibility with port f…
Browse files Browse the repository at this point in the history
  • Loading branch information
stephdl authored Jan 28, 2025
1 parent 58bcbdd commit 5cab769
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,17 @@ const restrictObjectsComboboxOptions = computed(() => {
label: t('standalone.port_forward.no_object')
}

const restrictOptions = props.restrictObjectSuggestions.map((obj) => {
return {
id: obj.id,
label: obj.name,
description: t(`standalone.objects.subtype_${obj.subtype}`),
icon: getObjectIcon(obj.subtype)
}
})
// filter out objects that contain other objects in their ipaddr
const restrictOptions = props.restrictObjectSuggestions
.filter((obj) => !obj.ipaddr?.some((ip: string) => ip.includes('objects/')))
.map((obj) => {
return {
id: obj.id,
label: obj.name,
description: t(`standalone.objects.subtype_${obj.subtype}`),
icon: getObjectIcon(obj.subtype)
}
})

return [noObjectOption, ...restrictOptions]
})
Expand Down Expand Up @@ -667,7 +670,15 @@ async function createOrEditPortForward() {
:selected-label="t('ne_combobox.selected')"
:user-input-label="t('ne_combobox.user_input_label')"
ref="destinationObjectRef"
/>
>
<template #tooltip>
<NeTooltip
><template #content>{{
t('standalone.port_forward.restricted_object_tooltip')
}}</template></NeTooltip
>
</template>
</NeCombobox>
<NeTextInput
v-if="!anyProtocolSelected"
:label="t('standalone.port_forward.destination_port')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { cloneDeep } from 'lodash-es'
import {
NeInlineNotification,
type NeComboboxOption,
Expand All @@ -20,6 +21,7 @@ import {
} from '@nethesis/vue-components'
import { ref, type PropType, watch, type Ref, computed } from 'vue'
import { ubusCall, ValidationError } from '@/lib/standalone/ubus'
import type { AxiosResponse } from 'axios'
import {
MessageBag,
validateAlphanumeric,
Expand All @@ -43,8 +45,21 @@ const props = defineProps({
}
})

type MatchInfo = {
database: string
family: 'ipv4' | 'ipv6'
id: string
name: string
type: string
}

type MatchInfoResponse = AxiosResponse<{
info: Record<string, MatchInfo>
}>

const emit = defineEmits(['close', 'reloadData'])

const portForwardsUsingHostSet = ref('')
const { t } = useI18n()
const name = ref('')
const nameRef = ref()
Expand Down Expand Up @@ -76,7 +91,13 @@ const ipVersionOptions = ref([
])

const recordOptionsButCurrent = computed(() => {
return props.recordOptions?.filter((option) => option.id !== props.currentHostSet?.id)
// Filter out objects from recordOptions based on the presence of an IP address with a hyphen in allObjects
const objectsWithHyphenIp = props.allObjects
.filter((obj) => obj.ipaddr.some((ip: string) => ip.includes('-')))
.map((obj) => obj.id)
return props.recordOptions?.filter(
(option) => option.id !== props.currentHostSet?.id && !objectsWithHyphenIp.includes(option.id)
)
})

const allObjectsButCurrent = computed(() => {
Expand All @@ -94,7 +115,7 @@ watch(
// editing host or host set
name.value = props.currentHostSet.name
ipVersion.value = props.currentHostSet.family as IpVersion
records.value = props.currentHostSet.ipaddr
records.value = cloneDeep(props.currentHostSet.ipaddr) // deep clone to avoid modifying the original array
} else {
// creating host or host set, reset form to defaults
name.value = ''
Expand All @@ -105,6 +126,18 @@ watch(
}
)

// compute portForwardsUsingHostSet the name of the portforward rule using this object
watch(
() => props.currentHostSet?.matches,
async (matches) => {
if (matches) {
portForwardsUsingHostSet.value = await getMatchedItemsName(matches)
} else {
portForwardsUsingHostSet.value = ''
}
}
)

function closeDrawer() {
emit('close')
}
Expand Down Expand Up @@ -133,6 +166,50 @@ function runFieldValidators(
return validators.every((validator) => validator.valid)
}

async function getMatchedItemsName(matches: string[]): Promise<string> {
try {
const res: MatchInfoResponse = await ubusCall('ns.objects', 'get-info', { ids: matches })
const names: string[] = []
for (const match of Object.values(res.data.info)) {
if (match.type == 'redirect') {
names.push(match.name)
}
}
return names.join(', ')
} catch (error: any) {
console.error('Error fetching getMatchedItemsName:', error)
return ''
}
}

function validateNoIpRangeWithPortForward(records: Array<string>) {
for (const record of records) {
if (record.includes('-') && portForwardsUsingHostSet.value) {
return {
valid: false,
errMessage: 'standalone.objects.range_not_compatible_with_port_forward'
}
}
}
return {
valid: true
}
}

function validateNoObjectsWithPortForward(records: Array<string>) {
for (const record of records) {
if (record.includes('objects/') && portForwardsUsingHostSet.value) {
return {
valid: false,
errMessage: 'standalone.objects.objects_are_not_compatible_with_port_forward'
}
}
}
return {
valid: true
}
}

function validateHostSetNotExists(value: string) {
if (allObjectsButCurrent.value?.find((obj) => obj.name === value && obj.subtype === 'host_set')) {
return {
Expand All @@ -158,7 +235,15 @@ function validate() {
nameRef
],
// records
[[validateRequired(records.value[0])], 'ipaddr', recordRef]
[
[
validateNoObjectsWithPortForward(records.value),
validateNoIpRangeWithPortForward(records.value),
validateRequired(records.value[0])
],
'ipaddr',
recordRef
]
]

// reset firstErrorRef for focus management
Expand Down Expand Up @@ -296,7 +381,11 @@ function deleteRecord(index: number) {
v-if="errorBag.getFirstI18nKeyFor('ipaddr')"
:class="'mt-2 text-sm text-rose-700 dark:text-rose-400'"
>
{{ t(errorBag.getFirstI18nKeyFor('ipaddr')) }}
{{
t(errorBag.getFirstI18nKeyFor('ipaddr'), {
name: portForwardsUsingHostSet
})
}}
</p>
<NeButton class="mt-4" size="md" @click="addRecord" kind="secondary">
<template #prefix>
Expand Down
1 change: 1 addition & 0 deletions src/composables/useObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ObjectReference = {
family: IpVersion
used: boolean
matches: string[]
ipaddr?: string[]
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@
"enter_restricted_addresses": "Enter restricted addresses",
"restricted_addresses": "Restricted addresses",
"restricted_object": "Restricted object",
"restricted_object_tooltip": "All objects supported, except host sets with IP ranges",
"restricted_object_tooltip": "All objects supported, except host sets containing IP ranges or nested objects",
"no_object": "No object",
"port_forwards_for_destination_name": "Port forwards for destination '{name}'"
},
Expand Down Expand Up @@ -2276,7 +2276,9 @@
"host_set_already_exists": "Host set already exists",
"domain_set_already_exists": "Domain set already exists",
"delete_domain_set": "Delete domain set",
"database_mwan3": "MultiWAN"
"database_mwan3": "MultiWAN",
"range_not_compatible_with_port_forward": "IP range is not compatible with port forwards. This host set is currently used by: {name}",
"objects_are_not_compatible_with_port_forward": "Objects are not compatible with port forwards. This host set is currently used by: {name}"
},
"ips": {
"title": "Intrusion Prevention System",
Expand Down

0 comments on commit 5cab769

Please sign in to comment.