Create frames for network filters management
This commit is contained in:
parent
22f5acd0ff
commit
c880c5e6bb
@ -27,6 +27,11 @@ import { BaseLoginPage } from "./widgets/BaseLoginPage";
|
|||||||
import { ViewNetworkRoute } from "./routes/ViewNetworkRoute";
|
import { ViewNetworkRoute } from "./routes/ViewNetworkRoute";
|
||||||
import { HomeRoute } from "./routes/HomeRoute";
|
import { HomeRoute } from "./routes/HomeRoute";
|
||||||
import { NetworkFiltersListRoute } from "./routes/NetworkFiltersListRoute";
|
import { NetworkFiltersListRoute } from "./routes/NetworkFiltersListRoute";
|
||||||
|
import { ViewNWFilterRoute } from "./routes/ViewNWFilterRoute";
|
||||||
|
import {
|
||||||
|
CreateNWFilterRoute,
|
||||||
|
EditNWFilterRoute,
|
||||||
|
} from "./routes/EditNWFilterRoute";
|
||||||
|
|
||||||
interface AuthContext {
|
interface AuthContext {
|
||||||
signedIn: boolean;
|
signedIn: boolean;
|
||||||
@ -63,6 +68,9 @@ export function App() {
|
|||||||
<Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
|
<Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
|
||||||
|
|
||||||
<Route path="nwfilter" element={<NetworkFiltersListRoute />} />
|
<Route path="nwfilter" element={<NetworkFiltersListRoute />} />
|
||||||
|
<Route path="nwfilter/new" element={<CreateNWFilterRoute />} />
|
||||||
|
<Route path="nwfilter/:uuid" element={<ViewNWFilterRoute />} />
|
||||||
|
<Route path="nwfilter/:uuid/edit" element={<EditNWFilterRoute />} />
|
||||||
|
|
||||||
<Route path="sysinfo" element={<SysInfoRoute />} />
|
<Route path="sysinfo" element={<SysInfoRoute />} />
|
||||||
<Route path="*" element={<NotFoundRoute />} />
|
<Route path="*" element={<NotFoundRoute />} />
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { APIClient } from "./ApiClient";
|
import { APIClient } from "./ApiClient";
|
||||||
|
import { ServerApi } from "./ServerApi";
|
||||||
|
|
||||||
export interface NWFilterChain {
|
export interface NWFilterChain {
|
||||||
protocol: string;
|
protocol: string;
|
||||||
@ -18,8 +19,91 @@ export interface NWFSMac {
|
|||||||
comment?: string;
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : complete
|
export interface NWFSArpOrRARP {
|
||||||
export type NWFSelector = NWFSAll | NWFSMac;
|
srcmacaddr?: string;
|
||||||
|
srcmacmask?: string;
|
||||||
|
dstmacaddr?: string;
|
||||||
|
dstmacmask?: string;
|
||||||
|
arpsrcipaddr?: string;
|
||||||
|
arpsrcipmask?: number;
|
||||||
|
arpdstipaddr?: string;
|
||||||
|
arpdstipmask?: number;
|
||||||
|
comment?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NWFSArp = NWFSArpOrRARP & {
|
||||||
|
type: "arp";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NWFSRArp = NWFSArpOrRARP & {
|
||||||
|
type: "rarp";
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface NWFSIPBase {
|
||||||
|
srcmacaddr?: string;
|
||||||
|
srcmacmask?: string;
|
||||||
|
dstmacaddr?: string;
|
||||||
|
dstmacmask?: string;
|
||||||
|
srcipaddr?: string;
|
||||||
|
srcipmask?: number;
|
||||||
|
dstipaddr?: string;
|
||||||
|
dstipmask?: number;
|
||||||
|
comment?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NFWSIPv4 = NWFSIPBase & { type: "ipv4" };
|
||||||
|
export type NFWSIPv6 = NWFSIPBase & { type: "ipv6" };
|
||||||
|
|
||||||
|
export type Layer4State =
|
||||||
|
| "NEW"
|
||||||
|
| "ESTABLISHED"
|
||||||
|
| "RELATED"
|
||||||
|
| "INVALID"
|
||||||
|
| "NONE";
|
||||||
|
|
||||||
|
export interface NWFSLayer4Base {
|
||||||
|
srcmacaddr?: string;
|
||||||
|
srcipaddr?: string;
|
||||||
|
srcipmask?: number;
|
||||||
|
dstipaddr?: string;
|
||||||
|
dstipmask?: number;
|
||||||
|
srcipfrom?: string;
|
||||||
|
srcipto?: string;
|
||||||
|
dstipfrom?: string;
|
||||||
|
dstipto?: string;
|
||||||
|
srcportstart?: number;
|
||||||
|
srcportend?: number;
|
||||||
|
dstportstart?: number;
|
||||||
|
dstportend?: number;
|
||||||
|
state?: Layer4State;
|
||||||
|
comment?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NFWSTCPv4 = NWFSLayer4Base & { type: "tcp" };
|
||||||
|
export type NFWSUDPv4 = NWFSLayer4Base & { type: "udp" };
|
||||||
|
export type NFWSSCTPv4 = NWFSLayer4Base & { type: "sctp" };
|
||||||
|
export type NFWSICMPv4 = NWFSLayer4Base & { type: "icmp" };
|
||||||
|
|
||||||
|
export type NFWSTCPv6 = NWFSLayer4Base & { type: "tcpipv6" };
|
||||||
|
export type NFWSUDPv6 = NWFSLayer4Base & { type: "udpipv6" };
|
||||||
|
export type NFWSSCTPv6 = NWFSLayer4Base & { type: "sctpipv6" };
|
||||||
|
export type NFWSICMPv6 = NWFSLayer4Base & { type: "icmpipv6" };
|
||||||
|
|
||||||
|
export type NWFSelector =
|
||||||
|
| NWFSAll
|
||||||
|
| NWFSMac
|
||||||
|
| NWFSArp
|
||||||
|
| NWFSRArp
|
||||||
|
| NFWSIPv4
|
||||||
|
| NFWSIPv6
|
||||||
|
| NFWSTCPv4
|
||||||
|
| NFWSUDPv4
|
||||||
|
| NFWSSCTPv4
|
||||||
|
| NFWSICMPv4
|
||||||
|
| NFWSTCPv6
|
||||||
|
| NFWSUDPv6
|
||||||
|
| NFWSSCTPv6
|
||||||
|
| NFWSICMPv6;
|
||||||
|
|
||||||
export interface NWFilterRule {
|
export interface NWFilterRule {
|
||||||
action: "drop" | "reject" | "accept" | "return" | "continue";
|
action: "drop" | "reject" | "accept" | "return" | "continue";
|
||||||
@ -41,6 +125,10 @@ export function NWFilterURL(n: NWFilter, edit: boolean = false): string {
|
|||||||
return `/nwfilter/${n.uuid}${edit ? "/edit" : ""}`;
|
return `/nwfilter/${n.uuid}${edit ? "/edit" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NWFilterIsBuiltin(n: NWFilter): boolean {
|
||||||
|
return ServerApi.Config.builtin_nwfilter_rules.includes(n.name);
|
||||||
|
}
|
||||||
|
|
||||||
export class NWFilterApi {
|
export class NWFilterApi {
|
||||||
/**
|
/**
|
||||||
* Get the entire list of networks
|
* Get the entire list of networks
|
||||||
@ -57,4 +145,64 @@ export class NWFilterApi {
|
|||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information about a single network filter
|
||||||
|
*/
|
||||||
|
static async GetSingle(uuid: string): Promise<NWFilter> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "GET",
|
||||||
|
uri: `/nwfilter/${uuid}`,
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the source XML configuration of a network filter for debugging purposes
|
||||||
|
*/
|
||||||
|
static async GetSingleXML(uuid: string): Promise<string> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
uri: `/nwfilter/${uuid}/src`,
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new network filter
|
||||||
|
*/
|
||||||
|
static async Create(n: NWFilter): Promise<{ uid: string }> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "POST",
|
||||||
|
uri: "/nwfilter/create",
|
||||||
|
jsonData: n,
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing network filter
|
||||||
|
*/
|
||||||
|
static async Update(n: NWFilter): Promise<{ uid: string }> {
|
||||||
|
return (
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "PUT",
|
||||||
|
uri: `/nwfilter/${n.uuid}`,
|
||||||
|
jsonData: n,
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a network filter
|
||||||
|
*/
|
||||||
|
static async Delete(n: NWFilter): Promise<void> {
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "DELETE",
|
||||||
|
uri: `/nwfilter/${n.uuid}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,12 +160,10 @@ export class NetworkApi {
|
|||||||
/**
|
/**
|
||||||
* Delete a network
|
* Delete a network
|
||||||
*/
|
*/
|
||||||
static async Delete(n: NetworkInfo): Promise<NetworkInfo[]> {
|
static async Delete(n: NetworkInfo): Promise<void> {
|
||||||
return (
|
await APIClient.exec({
|
||||||
await APIClient.exec({
|
method: "DELETE",
|
||||||
method: "DELETE",
|
uri: `/network/${n.uuid}`,
|
||||||
uri: `/network/${n.uuid}`,
|
});
|
||||||
})
|
|
||||||
).data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
150
virtweb_frontend/src/routes/EditNWFilterRoute.tsx
Normal file
150
virtweb_frontend/src/routes/EditNWFilterRoute.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { useAlert } from "../hooks/providers/AlertDialogProvider";
|
||||||
|
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
|
||||||
|
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
|
||||||
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
|
import { ConfigImportExportButtons } from "../widgets/ConfigImportExportButtons";
|
||||||
|
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||||
|
import { NWFilter, NWFilterApi, NWFilterURL } from "../api/NWFilterApi";
|
||||||
|
import { NWFilterDetails } from "../widgets/nwfilter/NWFilterDetails";
|
||||||
|
|
||||||
|
export function CreateNWFilterRoute(): React.ReactElement {
|
||||||
|
const alert = useAlert();
|
||||||
|
const snackbar = useSnackbar();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [nwfilter, setNWFilter] = React.useState<NWFilter>({
|
||||||
|
name: "my-filter",
|
||||||
|
join_filters: [],
|
||||||
|
rules: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const createNWFilter = async (n: NWFilter) => {
|
||||||
|
try {
|
||||||
|
const res = await NWFilterApi.Create(n);
|
||||||
|
snackbar("The network filter was successfully created!");
|
||||||
|
navigate(`/nwfilter/${res.uid}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert(`Failed to create network filter!\n${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditNetworkFilterRouteInner
|
||||||
|
nwfilter={nwfilter}
|
||||||
|
creating={true}
|
||||||
|
onCancel={() => navigate("/nwfilter")}
|
||||||
|
onSave={createNWFilter}
|
||||||
|
onReplace={setNWFilter}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EditNWFilterRoute(): React.ReactElement {
|
||||||
|
const alert = useAlert();
|
||||||
|
const snackbar = useSnackbar();
|
||||||
|
|
||||||
|
const { uuid } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [nwfilter, setNWFilter] = React.useState<NWFilter | undefined>();
|
||||||
|
|
||||||
|
const load = async () => {
|
||||||
|
setNWFilter(await NWFilterApi.GetSingle(uuid!));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateNetworkFilter = async (n: NWFilter) => {
|
||||||
|
try {
|
||||||
|
await NWFilterApi.Update(n);
|
||||||
|
snackbar("The network filter was successfully updated!");
|
||||||
|
navigate(NWFilterURL(nwfilter!));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert(`Failed to update network filter!\n${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncWidget
|
||||||
|
loadKey={uuid}
|
||||||
|
ready={nwfilter !== undefined}
|
||||||
|
errMsg="Failed to fetch network filter information!"
|
||||||
|
load={load}
|
||||||
|
build={() => (
|
||||||
|
<EditNetworkFilterRouteInner
|
||||||
|
nwfilter={nwfilter!}
|
||||||
|
creating={false}
|
||||||
|
onCancel={() => navigate(`/nwfilter/${uuid}`)}
|
||||||
|
onSave={updateNetworkFilter}
|
||||||
|
onReplace={setNWFilter}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditNetworkFilterRouteInner(p: {
|
||||||
|
nwfilter: NWFilter;
|
||||||
|
creating: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSave: (vm: NWFilter) => Promise<void>;
|
||||||
|
onReplace: (vm: NWFilter) => void;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const loadingMessage = useLoadingMessage();
|
||||||
|
|
||||||
|
const [changed, setChanged] = React.useState(false);
|
||||||
|
|
||||||
|
const [, updateState] = React.useState<any>();
|
||||||
|
const forceUpdate = React.useCallback(() => updateState({}), []);
|
||||||
|
|
||||||
|
const valueChanged = () => {
|
||||||
|
setChanged(true);
|
||||||
|
forceUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
loadingMessage.show("Saving network filter configuration...");
|
||||||
|
await p.onSave(p.nwfilter);
|
||||||
|
loadingMessage.hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtWebRouteContainer
|
||||||
|
label={p.creating ? "Create a Network Filter" : "Edit Network Filter"}
|
||||||
|
actions={
|
||||||
|
<span>
|
||||||
|
<ConfigImportExportButtons
|
||||||
|
currentConf={p.nwfilter}
|
||||||
|
filename={`nwfilter-${p.nwfilter.name}.json`}
|
||||||
|
importConf={(c) => {
|
||||||
|
p.onReplace(c);
|
||||||
|
valueChanged();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{changed && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={save}
|
||||||
|
style={{ marginRight: "10px" }}
|
||||||
|
>
|
||||||
|
{p.creating ? "Create" : "Save"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={p.onCancel} variant="outlined">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<NWFilterDetails
|
||||||
|
nwfilter={p.nwfilter}
|
||||||
|
editable={true}
|
||||||
|
onChange={valueChanged}
|
||||||
|
/>
|
||||||
|
</VirtWebRouteContainer>
|
||||||
|
);
|
||||||
|
}
|
@ -15,7 +15,12 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { NWFilter, NWFilterApi, NWFilterURL } from "../api/NWFilterApi";
|
import {
|
||||||
|
NWFilter,
|
||||||
|
NWFilterApi,
|
||||||
|
NWFilterIsBuiltin,
|
||||||
|
NWFilterURL,
|
||||||
|
} from "../api/NWFilterApi";
|
||||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
import { RouterLink } from "../widgets/RouterLink";
|
import { RouterLink } from "../widgets/RouterLink";
|
||||||
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||||
@ -61,10 +66,7 @@ function NetworkFiltersListRouteInner(p: {
|
|||||||
|
|
||||||
const onlyBuiltin = visibleFilters === VisibleFilters.Builtin;
|
const onlyBuiltin = visibleFilters === VisibleFilters.Builtin;
|
||||||
|
|
||||||
return p.list.filter(
|
return p.list.filter((f) => NWFilterIsBuiltin(f) === onlyBuiltin);
|
||||||
(f) =>
|
|
||||||
ServerApi.Config.builtin_nwfilter_rules.includes(f.name) === onlyBuiltin
|
|
||||||
);
|
|
||||||
}, [visibleFilters]);
|
}, [visibleFilters]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
65
virtweb_frontend/src/routes/ViewNWFilterRoute.tsx
Normal file
65
virtweb_frontend/src/routes/ViewNWFilterRoute.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
NWFilter,
|
||||||
|
NWFilterApi,
|
||||||
|
NWFilterIsBuiltin,
|
||||||
|
NWFilterURL,
|
||||||
|
} from "../api/NWFilterApi";
|
||||||
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
|
import { ConfigImportExportButtons } from "../widgets/ConfigImportExportButtons";
|
||||||
|
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||||
|
import { NWFilterDetails } from "../widgets/nwfilter/NWFilterDetails";
|
||||||
|
|
||||||
|
export function ViewNWFilterRoute() {
|
||||||
|
const { uuid } = useParams();
|
||||||
|
|
||||||
|
const [nwfilter, setNWFilter] = React.useState<NWFilter | undefined>();
|
||||||
|
|
||||||
|
const load = async () => {
|
||||||
|
setNWFilter(await NWFilterApi.GetSingle(uuid!));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncWidget
|
||||||
|
loadKey={uuid}
|
||||||
|
ready={nwfilter !== undefined}
|
||||||
|
errMsg="Failed to fetch network filter information!"
|
||||||
|
load={load}
|
||||||
|
build={() => <ViewNetworkFilterRouteInner nwfilter={nwfilter!} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ViewNetworkFilterRouteInner(p: {
|
||||||
|
nwfilter: NWFilter;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtWebRouteContainer
|
||||||
|
label={`Network filter ${p.nwfilter.name}`}
|
||||||
|
actions={
|
||||||
|
<span style={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<ConfigImportExportButtons
|
||||||
|
filename={`nwfilter-${p.nwfilter.name}.json`}
|
||||||
|
currentConf={p.nwfilter}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{!NWFilterIsBuiltin(p.nwfilter) && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
style={{ marginLeft: "15px" }}
|
||||||
|
onClick={() => navigate(NWFilterURL(p.nwfilter, true))}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<NWFilterDetails nwfilter={p.nwfilter} editable={false} />
|
||||||
|
</VirtWebRouteContainer>
|
||||||
|
);
|
||||||
|
}
|
12
virtweb_frontend/src/widgets/nwfilter/NWFilterDetails.tsx
Normal file
12
virtweb_frontend/src/widgets/nwfilter/NWFilterDetails.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { ReactElement } from "react";
|
||||||
|
import { NWFilter } from "../../api/NWFilterApi";
|
||||||
|
|
||||||
|
interface DetailsProps {
|
||||||
|
nwfilter: NWFilter;
|
||||||
|
editable: boolean;
|
||||||
|
onChange?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NWFilterDetails(p: DetailsProps): ReactElement {
|
||||||
|
return <>todo</>;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user