Create network filters route
This commit is contained in:
parent
706bce0fd8
commit
22f5acd0ff
19
virtweb_backend/Cargo.lock
generated
19
virtweb_backend/Cargo.lock
generated
@ -2159,18 +2159,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-xml-rs"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"serde",
|
|
||||||
"thiserror",
|
|
||||||
"xml-rs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.193"
|
version = "1.0.193"
|
||||||
@ -2677,7 +2665,6 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-xml-rs",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@ -2981,12 +2968,6 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xml-rs"
|
|
||||||
version = "0.8.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.31"
|
version = "0.7.31"
|
||||||
|
@ -22,7 +22,6 @@ actix-web-actors = "4.2.0"
|
|||||||
actix-http = "3.4.0"
|
actix-http = "3.4.0"
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
serde = { version = "1.0.193", features = ["derive"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
serde-xml-rs = "0.6.0"
|
|
||||||
quick-xml = { version = "0.31.0", features = ["serialize", "overlapped-lists"] }
|
quick-xml = { version = "0.31.0", features = ["serialize", "overlapped-lists"] }
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
|
@ -16,7 +16,7 @@ struct StaticConfig {
|
|||||||
iso_mimetypes: &'static [&'static str],
|
iso_mimetypes: &'static [&'static str],
|
||||||
net_mac_prefix: &'static str,
|
net_mac_prefix: &'static str,
|
||||||
constraints: ServerConstraints,
|
constraints: ServerConstraints,
|
||||||
builtin_network_rules: &'static [&'static str],
|
builtin_nwfilter_rules: &'static [&'static str],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
@ -46,7 +46,7 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
|
|||||||
oidc_auth_enabled: !AppConfig::get().disable_oidc,
|
oidc_auth_enabled: !AppConfig::get().disable_oidc,
|
||||||
iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
|
iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
|
||||||
net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
|
net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
|
||||||
builtin_network_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
|
builtin_nwfilter_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
|
||||||
constraints: ServerConstraints {
|
constraints: ServerConstraints {
|
||||||
iso_max_size: constants::ISO_MAX_SIZE,
|
iso_max_size: constants::ISO_MAX_SIZE,
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
|||||||
import { BaseLoginPage } from "./widgets/BaseLoginPage";
|
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";
|
||||||
|
|
||||||
interface AuthContext {
|
interface AuthContext {
|
||||||
signedIn: boolean;
|
signedIn: boolean;
|
||||||
@ -61,6 +62,8 @@ export function App() {
|
|||||||
<Route path="net/:uuid" element={<ViewNetworkRoute />} />
|
<Route path="net/:uuid" element={<ViewNetworkRoute />} />
|
||||||
<Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
|
<Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
|
||||||
|
|
||||||
|
<Route path="nwfilter" element={<NetworkFiltersListRoute />} />
|
||||||
|
|
||||||
<Route path="sysinfo" element={<SysInfoRoute />} />
|
<Route path="sysinfo" element={<SysInfoRoute />} />
|
||||||
<Route path="*" element={<NotFoundRoute />} />
|
<Route path="*" element={<NotFoundRoute />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -37,6 +37,10 @@ export interface NWFilter {
|
|||||||
rules: NWFilterRule[];
|
rules: NWFilterRule[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NWFilterURL(n: NWFilter, edit: boolean = false): string {
|
||||||
|
return `/nwfilter/${n.uuid}${edit ? "/edit" : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
export class NWFilterApi {
|
export class NWFilterApi {
|
||||||
/**
|
/**
|
||||||
* Get the entire list of networks
|
* Get the entire list of networks
|
||||||
|
@ -39,10 +39,6 @@ export function NetworkURL(n: NetworkInfo, edit: boolean = false): string {
|
|||||||
return `/net/${n.uuid}${edit ? "/edit" : ""}`;
|
return `/net/${n.uuid}${edit ? "/edit" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NetworkXMLURL(n: NetworkInfo): string {
|
|
||||||
return `/net/${n.uuid}/xml`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NetworkApi {
|
export class NetworkApi {
|
||||||
/**
|
/**
|
||||||
* Create a new network
|
* Create a new network
|
||||||
|
@ -7,6 +7,7 @@ export interface ServerConfig {
|
|||||||
iso_mimetypes: string[];
|
iso_mimetypes: string[];
|
||||||
net_mac_prefix: string;
|
net_mac_prefix: string;
|
||||||
constraints: ServerConstraints;
|
constraints: ServerConstraints;
|
||||||
|
builtin_nwfilter_rules: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServerConstraints {
|
export interface ServerConstraints {
|
||||||
|
@ -133,10 +133,6 @@ export class VMInfo implements VMInfoInterface {
|
|||||||
get VNCURL(): string {
|
get VNCURL(): string {
|
||||||
return `/vm/${this.uuid}/vnc`;
|
return `/vm/${this.uuid}/vnc`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get XMLURL(): string {
|
|
||||||
return `/vm/${this.uuid}/xml`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VMApi {
|
export class VMApi {
|
||||||
|
153
virtweb_frontend/src/routes/NetworkFiltersListRoute.tsx
Normal file
153
virtweb_frontend/src/routes/NetworkFiltersListRoute.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
Paper,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
ToggleButton,
|
||||||
|
ToggleButtonGroup,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { NWFilter, NWFilterApi, NWFilterURL } from "../api/NWFilterApi";
|
||||||
|
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||||
|
import { RouterLink } from "../widgets/RouterLink";
|
||||||
|
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
|
||||||
|
import { ServerApi } from "../api/ServerApi";
|
||||||
|
|
||||||
|
export function NetworkFiltersListRoute(): React.ReactElement {
|
||||||
|
const [list, setList] = React.useState<NWFilter[] | undefined>();
|
||||||
|
|
||||||
|
const [count] = React.useState(1);
|
||||||
|
|
||||||
|
const load = async () => {
|
||||||
|
setList(await NWFilterApi.GetList());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AsyncWidget
|
||||||
|
loadKey={count}
|
||||||
|
load={load}
|
||||||
|
ready={list !== undefined}
|
||||||
|
errMsg="Failed to load the list of networks!"
|
||||||
|
build={() => <NetworkFiltersListRouteInner list={list!} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VisibleFilters {
|
||||||
|
All,
|
||||||
|
Builtin,
|
||||||
|
Custom,
|
||||||
|
}
|
||||||
|
|
||||||
|
function NetworkFiltersListRouteInner(p: {
|
||||||
|
list: NWFilter[];
|
||||||
|
}): React.ReactElement {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [visibleFilters, setVisibleFilters] = React.useState(
|
||||||
|
VisibleFilters.All
|
||||||
|
);
|
||||||
|
|
||||||
|
const filteredList = React.useMemo(() => {
|
||||||
|
if (visibleFilters === VisibleFilters.All) return p.list;
|
||||||
|
|
||||||
|
const onlyBuiltin = visibleFilters === VisibleFilters.Builtin;
|
||||||
|
|
||||||
|
return p.list.filter(
|
||||||
|
(f) =>
|
||||||
|
ServerApi.Config.builtin_nwfilter_rules.includes(f.name) === onlyBuiltin
|
||||||
|
);
|
||||||
|
}, [visibleFilters]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtWebRouteContainer
|
||||||
|
label="Network filters"
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
<span style={{ flex: 10 }}></span>
|
||||||
|
<ToggleButtonGroup
|
||||||
|
size="small"
|
||||||
|
value={visibleFilters}
|
||||||
|
exclusive
|
||||||
|
onChange={(_ev, v) => setVisibleFilters(v)}
|
||||||
|
aria-label="visible filters"
|
||||||
|
>
|
||||||
|
<ToggleButton value={VisibleFilters.All}>All</ToggleButton>
|
||||||
|
<ToggleButton value={VisibleFilters.Builtin}>Builtin</ToggleButton>
|
||||||
|
<ToggleButton value={VisibleFilters.Custom}>Custom</ToggleButton>
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
<span style={{ flex: 2 }}></span>
|
||||||
|
|
||||||
|
<RouterLink to="/nwfilter/new">
|
||||||
|
<Button>New</Button>
|
||||||
|
</RouterLink>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Name</TableCell>
|
||||||
|
<TableCell>Chain</TableCell>
|
||||||
|
<TableCell>Priority</TableCell>
|
||||||
|
<TableCell>Referenced filters</TableCell>
|
||||||
|
<TableCell># of rules</TableCell>
|
||||||
|
<TableCell>Actions</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{filteredList.map((t) => {
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={t.uuid}
|
||||||
|
hover
|
||||||
|
onDoubleClick={() => navigate(NWFilterURL(t))}
|
||||||
|
>
|
||||||
|
<TableCell>{t.name}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{t.chain?.protocol ?? (
|
||||||
|
<Typography style={{ fontStyle: "italic" }}>
|
||||||
|
None
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{t.priority ?? (
|
||||||
|
<Typography style={{ fontStyle: "italic" }}>
|
||||||
|
None
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<ul>
|
||||||
|
{t.join_filters.map((f, n) => (
|
||||||
|
<li key={n}>{f}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{t.rules.length}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<RouterLink to={NWFilterURL(t)}>
|
||||||
|
<IconButton>
|
||||||
|
<VisibilityIcon />
|
||||||
|
</IconButton>
|
||||||
|
</RouterLink>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</VirtWebRouteContainer>
|
||||||
|
);
|
||||||
|
}
|
5
virtweb_frontend/src/utils/DebugUtils.ts
Normal file
5
virtweb_frontend/src/utils/DebugUtils.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export function isDebug(): boolean {
|
||||||
|
return (
|
||||||
|
!import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === "development"
|
||||||
|
);
|
||||||
|
}
|
@ -3,7 +3,9 @@ import {
|
|||||||
mdiDisc,
|
mdiDisc,
|
||||||
mdiHome,
|
mdiHome,
|
||||||
mdiInformation,
|
mdiInformation,
|
||||||
mdiLan
|
mdiLan,
|
||||||
|
mdiSecurity,
|
||||||
|
mdiSecurityNetwork,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import Icon from "@mdi/react";
|
import Icon from "@mdi/react";
|
||||||
import {
|
import {
|
||||||
@ -17,6 +19,7 @@ import {
|
|||||||
import { Outlet, useLocation } from "react-router-dom";
|
import { Outlet, useLocation } from "react-router-dom";
|
||||||
import { RouterLink } from "./RouterLink";
|
import { RouterLink } from "./RouterLink";
|
||||||
import { VirtWebAppBar } from "./VirtWebAppBar";
|
import { VirtWebAppBar } from "./VirtWebAppBar";
|
||||||
|
import { isDebug } from "../utils/DebugUtils";
|
||||||
|
|
||||||
export function BaseAuthenticatedPage(): React.ReactElement {
|
export function BaseAuthenticatedPage(): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
@ -60,6 +63,14 @@ export function BaseAuthenticatedPage(): React.ReactElement {
|
|||||||
uri="/net"
|
uri="/net"
|
||||||
icon={<Icon path={mdiLan} size={1} />}
|
icon={<Icon path={mdiLan} size={1} />}
|
||||||
/>
|
/>
|
||||||
|
{/* TODO : remove debug marker */}
|
||||||
|
{isDebug() && (
|
||||||
|
<NavLink
|
||||||
|
label="Network filters"
|
||||||
|
uri="/nwfilter"
|
||||||
|
icon={<Icon path={mdiSecurityNetwork} size={1} />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<NavLink
|
<NavLink
|
||||||
label="ISO files"
|
label="ISO files"
|
||||||
uri="/iso"
|
uri="/iso"
|
||||||
|
Loading…
Reference in New Issue
Block a user