diff --git a/ui/src/app/edge/settings/network/network.component.ts b/ui/src/app/edge/settings/network/network.component.ts index 29e63aeb7a1..98196777773 100644 --- a/ui/src/app/edge/settings/network/network.component.ts +++ b/ui/src/app/edge/settings/network/network.component.ts @@ -9,6 +9,7 @@ import { PipeComponentsModule } from "src/app/shared/pipe/pipe.module"; import { LiveDataServiceProvider } from "src/app/shared/provider/live-data-service-provider"; import { LocaleProvider } from "src/app/shared/provider/locale-provider"; import { Role } from "src/app/shared/type/role"; +import { InetUtils } from "src/app/shared/utils/inet/inet.utils"; import { CommonUiModule } from "../../../shared/common-ui.module"; import { Edge, Service, Websocket } from "../../../shared/shared"; import { GetNetworkConfigRequest } from "./getNetworkConfigRequest"; @@ -42,7 +43,6 @@ export class NetworkComponent implements OnInit { public edge: Edge | null = null; protected forms: InterfaceForm[] = []; - protected ipRegex: RegExp = /^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}\/(?:3[0-2]|[0-2]?[0-9])$/; constructor( private translate: TranslateService, @@ -160,7 +160,7 @@ export class NetworkComponent implements OnInit { if (iface.model.addressesList) { for (const addr of iface.model.addressesList) { - if (!this.ipRegex.test(addr)) { + if (InetUtils.isNetworkAddress(addr) !== InetUtils.IpType.IPv4) { this.service.toast(this.translate.instant("EDGE.NETWORK.VALID_ADDRESS_WARNING"), "danger"); return []; } diff --git a/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts b/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts index 0b7d0923b18..df1d9430112 100644 --- a/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts +++ b/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts @@ -1,18 +1,19 @@ // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; -import { FormBuilder, FormControl, FormGroup } from "@angular/forms"; +import { AbstractControl, FormBuilder, FormControl, FormGroup } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { FormlyFieldConfig, FormlyFormOptions } from "@ngx-formly/core"; import { TranslateService } from "@ngx-translate/core"; import { ComponentJsonApiRequest } from "src/app/shared/jsonrpc/request/componentJsonApiRequest"; import { ExecuteSystemCommandRequest } from "src/app/shared/jsonrpc/request/executeCommandRequest"; import { ExecuteSystemCommandResponse } from "src/app/shared/jsonrpc/response/executeSystemCommandResponse"; +import { InetUtils } from "src/app/shared/utils/inet/inet.utils"; import { Service, Utils, Websocket } from "../../../shared/shared"; type CommandFunction = (...args: (string | boolean | number)[]) => string; const COMMANDS: { [key: string]: CommandFunction; } = { - "ping": (ip: string) => `ping -c4 ${ip}`, + "ping": (host: string) => `ping -c4 ${host}`, "openems-restart": () => "which at || DEBIAN_FRONTEND=noninteractive apt-get -y install at; echo 'systemctl restart openems' | at now", }; @@ -42,14 +43,15 @@ export class SystemExecuteComponent implements OnInit { key: "ping", hideExpression: (model: any, formState: any) => this.model["predefined"] !== "ping", fieldGroup: [{ - key: "ip", + key: "host", type: "input", templateOptions: { - label: "IP-Address", placeholder: "192.168.0.1", required: true, pattern: /(\d{1,3}\.){3}\d{1,3}/, + label: "Host", placeholder: "127.0.0.1 / localhost", required: true, }, - validation: { - messages: { - pattern: (error, field: FormlyFieldConfig) => `"${field.formControl.value}" is not a valid IP Address`, + validators: { + host: { + expression: (c: AbstractControl) => InetUtils.isHostnameOrIp(c.value), + message: (error, field) => `${field.formControl?.value} is not a valid IP-Address or Hostname`, }, }, }], @@ -144,7 +146,7 @@ export class SystemExecuteComponent implements OnInit { const cmd = COMMANDS[m.predefined]; switch (m.predefined) { case "ping": - command = cmd(m.ping.ip); + command = cmd(m.ping.host); break; case "openems-restart": default: @@ -173,24 +175,25 @@ export class SystemExecuteComponent implements OnInit { command: command.value, }); - edge.sendRequest(this.websocket, - new ComponentJsonApiRequest({ - componentId: "_host", - payload: executeSystemCommandRequest, - })).then(response => { - const result = (response as ExecuteSystemCommandResponse).result; - this.loading = false; - if (result.stdout.length == 0) { - this.stdout = [""]; - } else { - this.stdout = result.stdout; - } - this.stderr = result.stderr; - - }).catch(reason => { - this.loading = false; - this.stderr = ["Error executing system command:", reason.error.message]; + const request = new ComponentJsonApiRequest({ + componentId: "_host", + payload: executeSystemCommandRequest, }); + edge.sendRequest(this.websocket, request) + .then(response => { + const result = (response as ExecuteSystemCommandResponse).result; + this.loading = false; + if (result.stdout.length == 0) { + this.stdout = [""]; + } else { + this.stdout = result.stdout; + } + this.stderr = result.stderr; + + }).catch(reason => { + this.loading = false; + this.stderr = ["Error executing system command:", reason.error.message]; + }); this.commandLogs.unshift(executeSystemCommandRequest); }); } diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index 9b04392cc75..35a6cc89357 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -58,6 +58,7 @@ import { RouteService } from "./service/route.service"; import { Service } from "./service/service"; import { Utils, Websocket } from "./shared"; import { Language } from "./type/language"; +import { InetUtils } from "./utils/inet/inet.utils"; export function registerTranslateExtension(translate: TranslateService) { @@ -74,17 +75,16 @@ export function registerTranslateExtension(translate: TranslateService) { }; } - -export function IpValidator(control: FormControl): ValidationErrors { - return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(control.value) ? null : { "ip": true }; +export function Ipv4Validator(control: FormControl): ValidationErrors { + return InetUtils.isIPv4(control.value) ? null : { "ip": true }; } export function SubnetmaskValidator(control: FormControl): ValidationErrors { - return /^(255)\.(0|128|192|224|240|248|252|254|255)\.(0|128|192|224|240|248|252|254|255)\.(0|128|192|224|240|248|252|254|255)/.test(control.value) ? null : { "subnetmask": true }; + return InetUtils.isSubnetMask(control.value) ? null : { "subnetmask": true }; } -export function IpValidatorMessage(err, field: FormlyFieldConfig) { - return `"${field.formControl.value}" is not a valid IP Address`; +export function Ipv4ValidatorMessage(err, field: FormlyFieldConfig) { + return `"${field.formControl.value}" is not a valid IPv4-Address`; } export function SubnetmaskValidatorMessage(err, field: FormlyFieldConfig) { @@ -148,12 +148,12 @@ export function PersonNameProhibitedCharactersValidator(control: FormControl): V { name: "range", component: FormlyRangeTypeComponent }, ], validators: [ - { name: "ip", validation: IpValidator }, + { name: "ip", validation: Ipv4Validator }, { name: "subnetmask", validation: SubnetmaskValidator }, { name: "person-name-prohibited-characters", validation: PersonNameProhibitedCharactersValidator }, ], validationMessages: [ - { name: "ip", message: IpValidatorMessage }, + { name: "ip", message: Ipv4ValidatorMessage }, { name: "subnetmask", message: SubnetmaskValidatorMessage }, ], }), diff --git a/ui/src/app/shared/utils/inet/inet.utils.spec.ts b/ui/src/app/shared/utils/inet/inet.utils.spec.ts new file mode 100644 index 00000000000..4a23e9651fb --- /dev/null +++ b/ui/src/app/shared/utils/inet/inet.utils.spec.ts @@ -0,0 +1,158 @@ +import { InetUtils } from "./inet.utils"; + +const VALID_IPV4 = "192.168.0.1"; +const VALID_IPV6 = "2001:0db8:1234:5678:9999::ABCD:EFff"; +const VALID_HOSTNAME = "openems.io"; + +describe("InetUtils", () => { + + it("#isSubnetMask", () => { + expect(InetUtils.isSubnetMask("255.255.255.255")).toBeTrue(); + expect(InetUtils.isSubnetMask("255.255.255.0")).toBeTrue(); + expect(InetUtils.isSubnetMask("255.255.192.0")).toBeTrue(); + expect(InetUtils.isSubnetMask("255.240.0.0")).toBeTrue(); + expect(InetUtils.isSubnetMask("128.0.0.0")).toBeTrue(); + + expect(InetUtils.isSubnetMask("255.255.255.200")).toBeFalse(); + expect(InetUtils.isSubnetMask("255.255.255.255.255")).toBeFalse(); + expect(InetUtils.isSubnetMask("255.255.255")).toBeFalse(); + expect(InetUtils.isSubnetMask("255.0.255.0")).toBeFalse(); + expect(InetUtils.isSubnetMask("255.192.255.192")).toBeFalse(); + expect(InetUtils.isSubnetMask("0.0.0.0")).toBeFalse(); + expect(InetUtils.isSubnetMask("255:255:255:255")).toBeFalse(); + expect(InetUtils.isSubnetMask(null)).toBeFalse(); + }); + + it("#isIpv4", () => { + expect(InetUtils.isIPv4(VALID_IPV4)).toBeTrue(); + expect(InetUtils.isIPv4(VALID_IPV6)).toBeFalse(); + expect(InetUtils.isIPv4(VALID_HOSTNAME)).toBeFalse(); + + expect(InetUtils.isIPv4("001.001.001.001")).toBeTrue(); + + expect(InetUtils.isIPv4("1.1.1.1.1")).toBeFalse(); + expect(InetUtils.isIPv4("1.1.1")).toBeFalse(); + expect(InetUtils.isIPv4("10.0.0.256")).toBeFalse(); + expect(InetUtils.isIPv4("1.0.0.1111")).toBeFalse(); + expect(InetUtils.isIPv4("1:1:1:1")).toBeFalse(); + }); + + it("#isIpv6", () => { + expect(InetUtils.isIPv6(VALID_IPV4)).toBeFalse(); + expect(InetUtils.isIPv6(VALID_IPV6)).toBeTrue(); + expect(InetUtils.isIPv6(VALID_HOSTNAME)).toBeFalse(); + + expect(InetUtils.isIPv6("::")).toBeTrue(); + expect(InetUtils.isIPv6("::1")).toBeTrue(); + expect(InetUtils.isIPv6("1::1")).toBeTrue(); + + expect(InetUtils.isIPv6("1:1:1:1")).toBeFalse(); + expect(InetUtils.isIPv6("1::defg")).toBeFalse(); + expect(InetUtils.isIPv6("1111::2222::3333")).toBeFalse(); + }); + + it("#isIp", () => { + expect(InetUtils.isIP(VALID_IPV4)).toBe(InetUtils.IpType.IPv4); + expect(InetUtils.isIP(VALID_IPV6)).toBe(InetUtils.IpType.IPv6); + expect(InetUtils.isIP(VALID_HOSTNAME)).toBe(InetUtils.IpType.None); + + expect(InetUtils.isIP("001.001.001.001")).toBe(InetUtils.IpType.IPv4); + expect(InetUtils.isIP("::")).toBe(InetUtils.IpType.IPv6); + expect(InetUtils.isIP("::1")).toBe(InetUtils.IpType.IPv6); + expect(InetUtils.isIP("1::1")).toBe(InetUtils.IpType.IPv6); + + expect(InetUtils.isIP("localhost")).toBe(InetUtils.IpType.None); + expect(InetUtils.isIP("")).toBe(InetUtils.IpType.None); + expect(InetUtils.isIP(null)).toBe(InetUtils.IpType.None); + }); + + it("#isValidIp", () => { + expect(InetUtils.isValidIP(VALID_IPV4)).toBeTrue(); + expect(InetUtils.isValidIP(VALID_IPV6)).toBeTrue(); + expect(InetUtils.isValidIP(VALID_HOSTNAME)).toBeFalse(); + + expect(InetUtils.isValidIP("001.001.001.001")).toBeTrue(); + expect(InetUtils.isValidIP("::")).toBeTrue(); + expect(InetUtils.isValidIP("::1")).toBeTrue(); + expect(InetUtils.isValidIP("1::1")).toBeTrue(); + + expect(InetUtils.isValidIP("localhost")).toBeFalse(); + expect(InetUtils.isValidIP("")).toBeFalse(); + expect(InetUtils.isValidIP(null)).toBeFalse(); + }); + + it("#checkHostname", () => { + expect(InetUtils.isHostname(VALID_IPV4)).toBeFalse(); + expect(InetUtils.isHostname(VALID_IPV6)).toBeFalse(); + expect(InetUtils.isHostname(VALID_HOSTNAME)).toBeTrue(); + + expect(InetUtils.isHostname("localhost")).toBeTrue(); + + expect(InetUtils.isHostname("1.test")).toBeTrue(); + expect(InetUtils.isHostname("1.1")).toBeFalse(); + expect(InetUtils.isHostname("local-test")).toBeTrue(); + expect(InetUtils.isHostname("-localtest")).toBeFalse(); + expect(InetUtils.isHostname("local.-test")).toBeFalse(); + expect(InetUtils.isHostname("a-b.c")).toBeTrue(); + expect(InetUtils.isHostname("a@b.c")).toBeFalse(); + expect(InetUtils.isHostname("openems.io.")).toBeTrue(); + expect(InetUtils.isHostname(".openems.io")).toBeFalse(); + expect(InetUtils.isHostname("1.1.1.1")).toBeFalse(); + expect(InetUtils.isHostname("256.256.256.256")).toBeFalse(); + expect(InetUtils.isHostname("1:1:1:1")).toBeFalse(); + expect(InetUtils.isHostname(null)).toBeFalse(); + }); + + it("#checkHostnameOrIp", () => { + expect(InetUtils.isHostnameOrIp(VALID_IPV4)).toBeTrue(); + expect(InetUtils.isHostnameOrIp(VALID_IPV6)).toBeTrue(); + expect(InetUtils.isHostnameOrIp(VALID_HOSTNAME)).toBeTrue(); + + // Localhost/Loopback + expect(InetUtils.isHostnameOrIp("127.0.0.1")).toBeTrue(); + expect(InetUtils.isHostnameOrIp("::1")).toBeTrue(); + expect(InetUtils.isHostnameOrIp("localhost")).toBeTrue(); + + // Any + expect(InetUtils.isHostnameOrIp("0.0.0.0")).toBeTrue(); + expect(InetUtils.isHostnameOrIp("::")).toBeTrue(); + + expect(InetUtils.isHostnameOrIp("a@b.c")).toBeFalse(); + expect(InetUtils.isHostnameOrIp("openems::io")).toBeFalse(); + expect(InetUtils.isHostnameOrIp(".openems.io")).toBeFalse(); + expect(InetUtils.isHostnameOrIp("1:1:1:1")).toBeFalse(); + expect(InetUtils.isHostnameOrIp(null)).toBeFalse(); + }); + + it("#isNetworkAddress", () => { + expect(InetUtils.isNetworkAddress("0.0.0.0/0")).toBe(InetUtils.IpType.IPv4); + expect(InetUtils.isNetworkAddress("1.1.1.1/24")).toBe(InetUtils.IpType.IPv4); + expect(InetUtils.isNetworkAddress("1::1/64")).toBe(InetUtils.IpType.IPv6);; + expect(InetUtils.isNetworkAddress("::/0")).toBe(InetUtils.IpType.IPv6); + + expect(InetUtils.isNetworkAddress("1.1.1.1/64")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress("1.1.1.1/test")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress("1::1/200")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress("1.1.1.1/4/8")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress("1.1.1.1")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress("localhost")).toBe(InetUtils.IpType.None); + expect(InetUtils.isNetworkAddress(null)).toBe(InetUtils.IpType.None); + }); + + + it("#isValidNetworkAddress", () => { + expect(InetUtils.isValidNetworkAddress("0.0.0.0/0")).toBeTrue(); + expect(InetUtils.isValidNetworkAddress("1.1.1.1/24")).toBeTrue(); + expect(InetUtils.isValidNetworkAddress("1::1/64")).toBeTrue(); + expect(InetUtils.isValidNetworkAddress("::/0")).toBeTrue(); + + expect(InetUtils.isValidNetworkAddress("1.1.1.1/64")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress("1.1.1.1/test")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress("1::1/200")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress("1.1.1.1/4/8")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress("1.1.1.1")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress("localhost")).toBeFalse(); + expect(InetUtils.isValidNetworkAddress(null)).toBeFalse(); + }); + +}); diff --git a/ui/src/app/shared/utils/inet/inet.utils.ts b/ui/src/app/shared/utils/inet/inet.utils.ts new file mode 100644 index 00000000000..ea8635fe562 --- /dev/null +++ b/ui/src/app/shared/utils/inet/inet.utils.ts @@ -0,0 +1,172 @@ +export namespace InetUtils { + export enum IpType { + None = 0, + IPv4 = 4, + IPv6 = 6, + } + + export const SUBNET_MASK_PATTERN: RegExp = /^(128|192|224|240|248|252|254|255)\.0\.0\.0$|^255\.(0|128|192|224|240|248|252|254|255)\.0\.0$|^255\.255\.(0|128|192|224|240|248|252|254|255)\.0$|^255\.255\.255\.(0|128|192|224|240|248|252|254|255)$/; + export const IPV4_PATTERN: RegExp = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + export const IPV6_PATTERN: RegExp = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:((:[0-9a-fA-F]{1,4}){1,7})|::|([0-9a-fA-F]{1,4}:){1}(:[0-9a-fA-F]{1,4}){1,6}|([0-9a-fA-F]{1,4}:){2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){6}:[0-9a-fA-F]{1,4})$/; + export const HOSTNAME_PATTERN: RegExp = /^([A-Za-z0-9][A-Za-z0-9-]*\.)*[A-Za-z][A-Za-z0-9-]*\.?$/; + + /** + * Checks whether a string is a valid IPv4 address. + * + * @param value the input string + * @returns true if the value is a valid IPv4 address + */ + export function isIPv4(value: string): boolean { + return value != null && IPV4_PATTERN.test(value); + } + + /** + * Checks whether a string is a valid IPv6 address. + * + * @param value the input string + * @returns true if the value is a valid IPv6 address + */ + export function isIPv6(value: string): boolean { + return value != null && IPV6_PATTERN.test(value); + } + + /** + * Checks whether a string is a valid IPv4 subnet mask. + * + * @param value the input string + * @returns true if the value is a valid subnet mask + */ + export function isSubnetMask(value: string): boolean { + return value != null && SUBNET_MASK_PATTERN.test(value); + } + + /** + * Detects whether a string is IPv4, IPv6 or neither. + * + * @param value the input string + * @returns the detected IP type + */ + export function isIP(value: string): IpType { + if (isIPv4(value)) { + return IpType.IPv4; + } + if (isIPv6(value)) { + return IpType.IPv6; + } + return IpType.None; + } + + /** + * Checks whether a string is either a valid IPv4 or IPv6 address. + * + * @param value the input string + * @returns true if the value is a valid IP address + */ + export function isValidIP(value: string): boolean { + return isIPv4(value) || isIPv6(value); + } + + /** + * Checks whether a string is a valid network address in CIDR notation. + * + * @param value the input string in the format "address/prefix" + * @returns the detected network address type + */ + export function isNetworkAddress(value: string): IpType { + if (value === null || value.length == 0) { return IpType.None; } + + const parts: string[] = value.split("/"); + if (parts.length != 2) { return IpType.None; } + + const cidrNum: number = Number.parseInt(parts[1], 10); + if (Number.isNaN(cidrNum)) { return IpType.None; } + + const ipType = isIP(parts[0]); + if (ipType === IpType.IPv4 && isValidIPv4Cidr(cidrNum)) { + return IpType.IPv4; + } + if (ipType === IpType.IPv6 && isValidIPv6Cidr(cidrNum)) { + return IpType.IPv6; + } + return IpType.None; + } + + /** + * Checks whether a string is a valid IPv4 or IPv6 network address in CIDR notation. + * + * @param value the input string + * @returns true if the value is a valid network address + */ + export function isValidNetworkAddress(value: string): boolean { + const type = isNetworkAddress(value); + return type === IpType.IPv4 || type === IpType.IPv6; + } + + /** + * Check if input string is a valid hostname according to RFC 1123 and RFC 952. + * As the syntax of Hostnames and IPv4-Addresses have a intersection, valid IPv4-Addresses are not considered hostnames. + * + * ```js + * InetUtils.isHostname('openems.io'); // returns true + * InetUtils.isHostname('localhost'); // returns true + * InetUtils.isHostname('127.0.0.1'); // returns false + * InetUtils.isHostname(''); // returns false + * ``` + * @param value to check for hostname + * @returns true if it is a valid hostename + */ + export function isHostname(value: string): boolean { + return value != null && HOSTNAME_PATTERN.test(value) && !isIPv4(value); + } + + /** + * Check if string represents a valid Hostname, IPv4 or IPv6. + * + * ```js + * InetUtils.isHostnameOrIp('openems.io'); // returns true + * InetUtils.isHostnameOrIp('localhost'); // returns true + * InetUtils.isHostnameOrIp('127.0.0.1'); // returns true + * InetUtils.isHostnameOrIp('::1'); // returns true + * InetUtils.isHostnameOrIp(''); // returns false + * ``` + * @param value to check + * @returns true if string is a valid Hostname, IPv4 or IPv6. + */ + export function isHostnameOrIp(value: string): boolean { + return isValidIP(value) || isHostname(value); + } + + /** + * Check if number is a valid CIDR. + * + * + * ```js + * InetUtils.isValidIPv4Cidr(24); // returns true + * InetUtils.isValidIPv4Cidr(-1); // returns false + * InetUtils.isValidIPv4Cidr(100); // returns false + * InetUtils.isValidIPv4Cidr(null); // returns false + * ``` + * @param value to check + * @returns true if valid ipv4 CIDR + */ + export function isValidIPv4Cidr(value: number): boolean { + return Number.isFinite(value) && value >= 0 && value <= 32; + } + + /** + * Check if number is a valid CIDR. + * + * + * ```js + * InetUtils.isValidIPv6Cidr(24); // returns true + * InetUtils.isValidIPv6Cidr(-1); // returns false + * InetUtils.isValidIPv6Cidr(200); // returns false + * InetUtils.isValidIPv6Cidr(null); // returns false + * ``` + * @param value to check + * @returns true if valid ipv6 CIDR + */ + export function isValidIPv6Cidr(value: number): boolean { + return Number.isFinite(value) && value >= 0 && value <= 128; + } +} diff --git a/ui/src/assets/i18n/cz.json b/ui/src/assets/i18n/cz.json index a561b899b7b..0d38e95b0f4 100644 --- a/ui/src/assets/i18n/cz.json +++ b/ui/src/assets/i18n/cz.json @@ -365,7 +365,7 @@ "MANDATORY_FIELDS": "Vyplňte prosím povinná pole", "SUBNETMASK": "Maska podsítě", "SUCCESS_UPDATE": "Konfigurace sítě pro doménu byla úspěšně aktualizována", - "VALID_ADDRESS_WARNING": "V části „Přidat statické IP adresy“ zadejte platnou adresu IP se síťovou maskou, např. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "V části „Přidat statické IP adresy“ zadejte platnou adresu IPv4 se síťovou maskou, např. 10.4.1.6/24" }, "SERVICE": { "CELL": { diff --git a/ui/src/assets/i18n/de.json b/ui/src/assets/i18n/de.json index 501d2bb7433..2080c1d4013 100644 --- a/ui/src/assets/i18n/de.json +++ b/ui/src/assets/i18n/de.json @@ -568,7 +568,7 @@ "SUBMIT": "Anwenden", "SUBNETMASK": "Subnetzmaske", "SUCCESS_UPDATE": "Netzwerkkonfiguration erfolgreich aktualisiert für", - "VALID_ADDRESS_WARNING": "Geben Sie unter \"Statische IP-Adressen hinzufügen\" eine gültige IP-Adresse mit Netzmaske ein, z. B. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "Geben Sie unter \"Statische IP-Adressen hinzufügen\" eine gültige IPv4-Adresse mit Netzmaske ein, z. B. 10.4.1.6/24" }, "SERVICE": { "CELL": { diff --git a/ui/src/assets/i18n/en.json b/ui/src/assets/i18n/en.json index 5d06fc93ac9..04e12f8ed18 100644 --- a/ui/src/assets/i18n/en.json +++ b/ui/src/assets/i18n/en.json @@ -579,7 +579,7 @@ "SUBMIT": "Submit", "SUBNETMASK": "Subnetmask", "SUCCESS_UPDATE": "Successfully updated network configuration for", - "VALID_ADDRESS_WARNING": "In 'Add static IP addresses', Please enter valid ip address with subnetmask e.g. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "In 'Add static IP addresses', Please enter valid IPv4 address with subnetmask e.g. 10.4.1.6/24" }, "SERVICE": { "CELL": { diff --git a/ui/src/assets/i18n/es.json b/ui/src/assets/i18n/es.json index e0ddf929df6..be96c80f0a7 100644 --- a/ui/src/assets/i18n/es.json +++ b/ui/src/assets/i18n/es.json @@ -362,7 +362,7 @@ "MANDATORY_FIELDS": "Por favor complete los campos obligatorios", "SUBNETMASK": "Máscara de subred", "SUCCESS_UPDATE": "Configuración de red actualizada con éxito para", - "VALID_ADDRESS_WARNING": "En 'Agregar direcciones IP estáticas', ingrese una dirección IP válida con máscara de red, p. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "En 'Agregar direcciones IP estáticas', ingrese una dirección IPv4 válida con máscara de red, p. 10.4.1.6/24" }, "SERVICE": { "CELL": { diff --git a/ui/src/assets/i18n/fr.json b/ui/src/assets/i18n/fr.json index 65cc96cdbb5..2ab472e5678 100644 --- a/ui/src/assets/i18n/fr.json +++ b/ui/src/assets/i18n/fr.json @@ -359,7 +359,7 @@ "MANDATORY_FIELDS": "Merci de remplir les champs obligatoires", "SUBNETMASK": "Masque de sous-réseau", "SUCCESS_UPDATE": "Configuration réseau mise à jour avec succès pour", - "VALID_ADDRESS_WARNING": "Dans 'Ajouter des adresses IP statiques', veuillez entrer une adresse IP valide avec un masque de réseau, par ex. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "Dans 'Ajouter des adresses IP statiques', veuillez entrer une adresse IPv4 valide avec un masque de réseau, par ex. 10.4.1.6/24" }, "SERVICE": { "CELL": { diff --git a/ui/src/assets/i18n/nl.json b/ui/src/assets/i18n/nl.json index 12b1789cbf4..60df8be9b7c 100644 --- a/ui/src/assets/i18n/nl.json +++ b/ui/src/assets/i18n/nl.json @@ -361,7 +361,7 @@ "MANDATORY_FIELDS": "Gelieve de verplichte velden in te vullen", "SUBNETMASK": "Subnetmasker", "SUCCESS_UPDATE": "Netwerkconfiguratie geüpdatet voor:", - "VALID_ADDRESS_WARNING": "Voer bij 'Statische IP-adressen toevoegen' een geldig ip-adres in met netmasker, b.v. 10.4.1.6/24" + "VALID_ADDRESS_WARNING": "Voer bij 'Statische IP-adressen toevoegen' een geldig IPv4-adres in met netmasker, b.v. 10.4.1.6/24" }, "SERVICE": { "CELL": {