Add API tokens support #9
| @@ -1,7 +1,9 @@ | ||||
| import { APIClient } from "./ApiClient"; | ||||
|  | ||||
| export type RightVerb = "POST" | "GET" | "PUT" | "DELETE" | "PATCH"; | ||||
|  | ||||
| export interface TokenRight { | ||||
|   verb: "POST" | "GET" | "PUT" | "DELETE" | "PATCH"; | ||||
|   verb: RightVerb; | ||||
|   path: string; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,8 @@ import { EditSection } from "../forms/EditSection"; | ||||
| import { IPInputWithMask } from "../forms/IPInput"; | ||||
| import { RadioGroupInput } from "../forms/RadioGroupInput"; | ||||
| import { TextInput } from "../forms/TextInput"; | ||||
| import { RawRightsEditor } from "./RawRightsEditor"; | ||||
| import { TokenRawRightsEditor } from "./TokenRawRightsEditor"; | ||||
| import { TokenRightsEditor } from "./TokenRightsEditor"; | ||||
|  | ||||
| const SECS_PER_DAY = 3600 * 24; | ||||
|  | ||||
| @@ -106,7 +107,7 @@ function APITokenDetailsInner(p: DetailsInnerProps): React.ReactElement { | ||||
|         ]} | ||||
|       /> | ||||
|       {currTab === TokenTab.General && <APITokenTabGeneral {...p} />} | ||||
|       {/* todo: rights */} | ||||
|       {currTab === TokenTab.Rights && <APITokenRights {...p} />} | ||||
|       {currTab === TokenTab.RawRights && <APITokenRawRights {...p} />} | ||||
|       {currTab === TokenTab.Danger && <APITokenTabDanger {...p} />} | ||||
|     </> | ||||
| @@ -198,10 +199,24 @@ function APITokenTabGeneral(p: DetailsInnerProps): React.ReactElement { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function APITokenRights(p: DetailsInnerProps): React.ReactElement { | ||||
|   return ( | ||||
|     <div style={{ padding: "30px" }}> | ||||
|       <TokenRightsEditor | ||||
|         {...p} | ||||
|         editable={p.status !== TokenWidgetStatus.Read} | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function APITokenRawRights(p: DetailsInnerProps): React.ReactElement { | ||||
|   return ( | ||||
|     <div style={{ padding: "30px" }}> | ||||
|       <RawRightsEditor {...p} editable={p.status !== TokenWidgetStatus.Read} /> | ||||
|       <TokenRawRightsEditor | ||||
|         {...p} | ||||
|         editable={p.status !== TokenWidgetStatus.Read} | ||||
|       /> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import { APIToken } from "../../api/TokensApi"; | ||||
| import { SelectInput } from "../forms/SelectInput"; | ||||
| import { TextInput } from "../forms/TextInput"; | ||||
| 
 | ||||
| export function RawRightsEditor(p: { | ||||
| export function TokenRawRightsEditor(p: { | ||||
|   token: APIToken; | ||||
|   editable: boolean; | ||||
|   onChange?: () => void; | ||||
							
								
								
									
										151
									
								
								virtweb_frontend/src/widgets/tokens/TokenRightsEditor.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								virtweb_frontend/src/widgets/tokens/TokenRightsEditor.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | ||||
| import { | ||||
|   Checkbox, | ||||
|   FormControlLabel, | ||||
|   Paper, | ||||
|   Tooltip, | ||||
|   Typography, | ||||
| } from "@mui/material"; | ||||
| import React from "react"; | ||||
| import { NWFilter } from "../../api/NWFilterApi"; | ||||
| import { NetworkInfo } from "../../api/NetworksApi"; | ||||
| import { APIToken, TokenRight } from "../../api/TokensApi"; | ||||
| import { VMInfo } from "../../api/VMApi"; | ||||
|  | ||||
| export function TokenRightsEditor(p: { | ||||
|   token: APIToken; | ||||
|   editable: boolean; | ||||
|   onChange?: () => void; | ||||
|   vms: VMInfo[]; | ||||
|   networks: NetworkInfo[]; | ||||
|   nwFilters: NWFilter[]; | ||||
|   tokens: APIToken[]; | ||||
| }): React.ReactElement { | ||||
|   return ( | ||||
|     <> | ||||
|       {/* ISO files */} | ||||
|       <RightsSection label="ISO files"> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "POST", path: "/api/iso/upload" }} | ||||
|           label="Upload a new ISO file" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "POST", path: "/api/iso/upload_from_url" }} | ||||
|           label="Upload a new ISO file from a given URL" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/iso/list" }} | ||||
|           label="Get the list of ISO files" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/iso/*" }} | ||||
|           label="Download ISO files" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "DELETE", path: "/api/iso/*" }} | ||||
|           label="Delete ISO files" | ||||
|         /> | ||||
|       </RightsSection> | ||||
|  | ||||
|       {/* Server general information */} | ||||
|       <RightsSection label="Server"> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/server/static_config" }} | ||||
|           label="Get static server configuration" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/server/info" }} | ||||
|           label="Get server information" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/server/network_hook_status" }} | ||||
|           label="Get network hook status" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/server/number_vcpus" }} | ||||
|           label="Get number of vCPU" | ||||
|         /> | ||||
|         <RouteRight | ||||
|           {...p} | ||||
|           right={{ verb: "GET", path: "/api/server/networks" }} | ||||
|           label="Get list of network cards" | ||||
|         /> | ||||
|       </RightsSection> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function RightsSection( | ||||
|   p: React.PropsWithChildren<{ label: string }> | ||||
| ): React.ReactElement { | ||||
|   return ( | ||||
|     <Paper style={{ padding: "20px" }}> | ||||
|       <Typography variant="h5">{p.label}</Typography> | ||||
|       {p.children} | ||||
|     </Paper> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function RouteRight(p: { | ||||
|   right: TokenRight; | ||||
|   label?: string; | ||||
|   editable: boolean; | ||||
|   token: APIToken; | ||||
|   onChange?: () => void; | ||||
|   parent?: TokenRight; | ||||
| }): React.ReactElement { | ||||
|   const activated = | ||||
|     p.token.rights.find( | ||||
|       (r) => r.verb === p.right.verb && r.path === p.right.path | ||||
|     ) !== undefined; | ||||
|  | ||||
|   const toggle = (a: boolean) => { | ||||
|     if (a) { | ||||
|       p.token.rights.push(p.right); | ||||
|     } else { | ||||
|       p.token.rights.splice(p.token.rights.indexOf(p.right), 1); | ||||
|     } | ||||
|     p.onChange?.(); | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <div> | ||||
|       <Tooltip | ||||
|         title={`${p.right.verb} ${p.right.path}`} | ||||
|         arrow | ||||
|         placement="left" | ||||
|         slotProps={{ | ||||
|           popper: { | ||||
|             modifiers: [ | ||||
|               { | ||||
|                 name: "offset", | ||||
|                 options: { | ||||
|                   offset: [0, -14], | ||||
|                 }, | ||||
|               }, | ||||
|             ], | ||||
|           }, | ||||
|         }} | ||||
|       > | ||||
|         <FormControlLabel | ||||
|           control={ | ||||
|             <Checkbox | ||||
|               checked={activated} | ||||
|               disabled={!p.editable} | ||||
|               onChange={(_e, a) => toggle(a)} | ||||
|             /> | ||||
|           } | ||||
|           label={p.label} | ||||
|         /> | ||||
|       </Tooltip> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user