Add API tokens support #9

Merged
pierre merged 40 commits from api into master 2024-04-23 17:04:45 +00:00
Showing only changes of commit c0b50c75fd - Show all commits

View File

@ -2,6 +2,11 @@ import {
Checkbox, Checkbox,
FormControlLabel, FormControlLabel,
Paper, Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Tooltip, Tooltip,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
@ -22,6 +27,215 @@ export function TokenRightsEditor(p: {
}): React.ReactElement { }): React.ReactElement {
return ( return (
<> <>
{/* Virtual machines */}
<RightsSection label="Virtual machines">
<RouteRight
{...p}
right={{ verb: "POST", path: "/api/vm/create" }}
label="Create a new virtual machine"
/>
<RouteRight
{...p}
right={{ verb: "GET", path: "/api/vm/list" }}
label="Get list of virtual machines"
/>
<RouteRight
{...p}
right={{ verb: "GET", path: "/api/vnc" }}
label="Establish VNC connection"
/>
</RightsSection>
<RightsSection label="VM configuration management">
<Table size="small">
<TableHead>
<TableRow>
<TableCell>VM name</TableCell>
<TableCell>Get definition</TableCell>
<TableCell>Update</TableCell>
<TableCell>Delete</TableCell>
<TableCell>Get XML definition</TableCell>
<TableCell>Get autostart</TableCell>
<TableCell>Set autostart</TableCell>
</TableRow>
</TableHead>
<TableBody>
{/* All VM operations */}
<TableRow hover>
<TableCell>
<i>All</i>
</TableCell>
<CellRight {...p} right={{ verb: "GET", path: "/api/vm/*" }} />
<CellRight {...p} right={{ verb: "PUT", path: "/api/vm/*" }} />
<CellRight {...p} right={{ verb: "DELETE", path: "/api/vm/*" }} />
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/src" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/autostart" }}
/>
<CellRight
{...p}
right={{ verb: "PUT", path: "/api/vm/*/autostart" }}
/>
</TableRow>
{/* Per VM operations */}
{p.vms.map((v, n) => (
<TableRow hover key={n}>
<TableCell>{v.name}</TableCell>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}` }}
parent={{ verb: "GET", path: "/api/vm/*" }}
/>
<CellRight
{...p}
right={{ verb: "PUT", path: `/api/vm/${v.uuid}` }}
parent={{ verb: "PUT", path: "/api/vm/*" }}
/>
<CellRight
{...p}
right={{ verb: "DELETE", path: `/api/vm/${v.uuid}` }}
parent={{ verb: "DELETE", path: "/api/vm/*" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/src` }}
parent={{ verb: "GET", path: "/api/vm/*/src" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/autostart` }}
parent={{ verb: "GET", path: "/api/vm/*/autostart" }}
/>
<CellRight
{...p}
right={{ verb: "PUT", path: `/api/vm/${v.uuid}/autostart` }}
parent={{ verb: "PUT", path: "/api/vm/*/autostart" }}
/>
</TableRow>
))}
</TableBody>
</Table>
</RightsSection>
<RightsSection label="VM maintenance">
<Table size="small">
<TableHead>
<TableRow>
<TableCell>VM name</TableCell>
<TableCell>Get state</TableCell>
<TableCell>Start</TableCell>
<TableCell>Shutdown</TableCell>
<TableCell>Kill</TableCell>
<TableCell>Reset</TableCell>
<TableCell>Suspend</TableCell>
<TableCell>Resume</TableCell>
<TableCell>Screenshot</TableCell>
<TableCell>VNC token</TableCell>
</TableRow>
</TableHead>
<TableBody>
{/* All VM operations */}
<TableRow hover>
<TableCell>
<i>All</i>
</TableCell>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/state" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/start" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/shutdown" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/kill" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/reset" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/suspend" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/resume" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/screenshot" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: "/api/vm/*/vnc" }}
/>
</TableRow>
{/* Per VM operations */}
{p.vms.map((v, n) => (
<TableRow hover key={n}>
<TableCell>{v.name}</TableCell>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/state` }}
parent={{ verb: "GET", path: "/api/vm/*/state" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/start` }}
parent={{ verb: "GET", path: "/api/vm/*/start" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/shutdown` }}
parent={{ verb: "GET", path: "/api/vm/*/shutdown" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/kill` }}
parent={{ verb: "GET", path: "/api/vm/*/kill" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/reset` }}
parent={{ verb: "GET", path: "/api/vm/*/reset" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/suspend` }}
parent={{ verb: "GET", path: "/api/vm/*/suspend" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/resume` }}
parent={{ verb: "GET", path: "/api/vm/*/resume" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/screenshot` }}
parent={{ verb: "GET", path: "/api/vm/*/screenshot" }}
/>
<CellRight
{...p}
right={{ verb: "GET", path: `/api/vm/${v.uuid}/vnc` }}
parent={{ verb: "GET", path: "/api/vm/*/vnc" }}
/>
</TableRow>
))}
</TableBody>
</Table>
</RightsSection>
{/* ISO files */} {/* ISO files */}
<RightsSection label="ISO files"> <RightsSection label="ISO files">
<RouteRight <RouteRight
@ -87,31 +301,47 @@ function RightsSection(
p: React.PropsWithChildren<{ label: string }> p: React.PropsWithChildren<{ label: string }>
): React.ReactElement { ): React.ReactElement {
return ( return (
<Paper style={{ padding: "20px" }}> <Paper style={{ padding: "20px", margin: "10px" }}>
<Typography variant="h5">{p.label}</Typography> <Typography variant="h5">{p.label}</Typography>
{p.children} {p.children}
</Paper> </Paper>
); );
} }
function RouteRight(p: { interface RightOpts {
right: TokenRight; right: TokenRight;
label?: string; label?: string;
editable: boolean; editable: boolean;
token: APIToken; token: APIToken;
onChange?: () => void; onChange?: () => void;
parent?: TokenRight; parent?: TokenRight;
}): React.ReactElement { }
const activated =
p.token.rights.find( function CellRight(p: RightOpts): React.ReactElement {
return (
<TableCell align="center">
<RouteRight {...p} />
</TableCell>
);
}
function RouteRight(p: RightOpts): React.ReactElement {
const rightIndex = p.token.rights.findIndex(
(r) => r.verb === p.right.verb && r.path === p.right.path (r) => r.verb === p.right.verb && r.path === p.right.path
) !== undefined; );
const activated = rightIndex !== -1;
const parentActivated =
!!p.parent &&
p.token.rights.findIndex(
(r) => r.verb === p.parent?.verb && r.path === p.parent?.path
) !== -1;
const toggle = (a: boolean) => { const toggle = (a: boolean) => {
if (a) { if (a) {
p.token.rights.push(p.right); p.token.rights.push(p.right);
} else { } else {
p.token.rights.splice(p.token.rights.indexOf(p.right), 1); p.token.rights.splice(rightIndex, 1);
} }
p.onChange?.(); p.onChange?.();
}; };
@ -138,8 +368,8 @@ function RouteRight(p: {
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={activated} checked={activated || parentActivated}
disabled={!p.editable} disabled={!p.editable || parentActivated}
onChange={(_e, a) => toggle(a)} onChange={(_e, a) => toggle(a)}
/> />
} }