Create network filters route

This commit is contained in:
Pierre HUBERT 2024-01-03 14:50:59 +01:00
parent 706bce0fd8
commit 22f5acd0ff
11 changed files with 180 additions and 31 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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,

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View 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>
);
}

View File

@ -0,0 +1,5 @@
export function isDebug(): boolean {
return (
!import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === "development"
);
}

View File

@ -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"