Skip to content
Open
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
4 changes: 2 additions & 2 deletions ui/src/app/edge/settings/network/network.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 [];
}
Expand Down
53 changes: 28 additions & 25 deletions ui/src/app/edge/settings/systemexecute/systemexecute.component.ts
Original file line number Diff line number Diff line change
@@ -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",
};

Expand Down Expand Up @@ -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`,
},
},
}],
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
});
}
Expand Down
16 changes: 8 additions & 8 deletions ui/src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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 },
],
}),
Expand Down
158 changes: 158 additions & 0 deletions ui/src/app/shared/utils/inet/inet.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
});

});
Loading