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 { HomeRoute } from "./routes/HomeRoute";
|
||||
import { NetworkFiltersListRoute } from "./routes/NetworkFiltersListRoute";
|
||||
import { ViewNWFilterRoute } from "./routes/ViewNWFilterRoute";
|
||||
import {
|
||||
CreateNWFilterRoute,
|
||||
EditNWFilterRoute,
|
||||
} from "./routes/EditNWFilterRoute";
|
||||
|
||||
interface AuthContext {
|
||||
signedIn: boolean;
|
||||
@ -63,6 +68,9 @@ export function App() {
|
||||
<Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
|
||||
|
||||
<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="*" element={<NotFoundRoute />} />
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { APIClient } from "./ApiClient";
|
||||
import { ServerApi } from "./ServerApi";
|
||||
|
||||
export interface NWFilterChain {
|
||||
protocol: string;
|
||||
@ -18,8 +19,91 @@ export interface NWFSMac {
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
// TODO : complete
|
||||
export type NWFSelector = NWFSAll | NWFSMac;
|
||||
export interface NWFSArpOrRARP {
|
||||
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 {
|
||||
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" : ""}`;
|
||||
}
|
||||
|
||||
export function NWFilterIsBuiltin(n: NWFilter): boolean {
|
||||
return ServerApi.Config.builtin_nwfilter_rules.includes(n.name);
|
||||
}
|
||||
|
||||
export class NWFilterApi {
|
||||
/**
|
||||
* Get the entire list of networks
|
||||
@ -57,4 +145,64 @@ export class NWFilterApi {
|
||||
|
||||
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
|
||||
*/
|
||||
static async Delete(n: NetworkInfo): Promise<NetworkInfo[]> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
method: "DELETE",
|
||||
uri: `/network/${n.uuid}`,
|
||||
})
|
||||
).data;
|
||||
static async Delete(n: NetworkInfo): Promise<void> {
|
||||
await APIClient.exec({
|
||||
method: "DELETE",
|
||||
uri: `/network/${n.uuid}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
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";
|
||||
import React from "react";
|
||||
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 { RouterLink } from "../widgets/RouterLink";
|
||||
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||
@ -61,10 +66,7 @@ function NetworkFiltersListRouteInner(p: {
|
||||
|
||||
const onlyBuiltin = visibleFilters === VisibleFilters.Builtin;
|
||||
|
||||
return p.list.filter(
|
||||
(f) =>
|
||||
ServerApi.Config.builtin_nwfilter_rules.includes(f.name) === onlyBuiltin
|
||||
);
|
||||
return p.list.filter((f) => NWFilterIsBuiltin(f) === onlyBuiltin);
|
||||
}, [visibleFilters]);
|
||||
|
||||
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