Add raw API token rights editor
This commit is contained in:
		@@ -29,6 +29,7 @@ export interface ServerConstraints {
 | 
			
		||||
  nwfilter_selectors_count: LenConstraint;
 | 
			
		||||
  api_token_name_size: LenConstraint;
 | 
			
		||||
  api_token_description_size: LenConstraint;
 | 
			
		||||
  api_token_right_path_size: LenConstraint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface LenConstraint {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ export interface SelectOption {
 | 
			
		||||
export function SelectInput(p: {
 | 
			
		||||
  value?: string;
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  label: string;
 | 
			
		||||
  label?: string;
 | 
			
		||||
  options: SelectOption[];
 | 
			
		||||
  onValueChange: (o?: string) => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
@@ -29,7 +29,7 @@ export function SelectInput(p: {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <FormControl fullWidth variant="standard" style={{ marginBottom: "15px" }}>
 | 
			
		||||
      <InputLabel>{p.label}</InputLabel>
 | 
			
		||||
      {p.label && <InputLabel>{p.label}</InputLabel>}
 | 
			
		||||
      <Select
 | 
			
		||||
        value={p.value ?? ""}
 | 
			
		||||
        label={p.label}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import { EditSection } from "../forms/EditSection";
 | 
			
		||||
import { IPInputWithMask } from "../forms/IPInput";
 | 
			
		||||
import { RadioGroupInput } from "../forms/RadioGroupInput";
 | 
			
		||||
import { TextInput } from "../forms/TextInput";
 | 
			
		||||
import { RawRightsEditor } from "./RawRightsEditor";
 | 
			
		||||
 | 
			
		||||
const SECS_PER_DAY = 3600 * 24;
 | 
			
		||||
 | 
			
		||||
@@ -104,14 +105,15 @@ function APITokenDetailsInner(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
          },
 | 
			
		||||
        ]}
 | 
			
		||||
      />
 | 
			
		||||
      {currTab === TokenTab.General && <NetworkDetailsTabGeneral {...p} />}
 | 
			
		||||
      {currTab === TokenTab.General && <APITokenTabGeneral {...p} />}
 | 
			
		||||
      {/* todo: rights */}
 | 
			
		||||
      {currTab === TokenTab.RawRights && <APITokenRawRights {...p} />}
 | 
			
		||||
      {currTab === TokenTab.Danger && <APITokenTabDanger {...p} />}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function NetworkDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
function APITokenTabGeneral(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  const [ipVersion, setIpVersion] = React.useState<4 | 6>(
 | 
			
		||||
    (p.token.ip_restriction ?? "").includes(":") ? 6 : 4
 | 
			
		||||
  );
 | 
			
		||||
@@ -196,6 +198,14 @@ function NetworkDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function APITokenRawRights(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={{ padding: "30px" }}>
 | 
			
		||||
      <RawRightsEditor {...p} editable={p.status !== TokenWidgetStatus.Read} />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function APITokenTabDanger(p: DetailsInnerProps): React.ReactElement {
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
  const snackbar = useSnackbar();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										111
									
								
								virtweb_frontend/src/widgets/tokens/RawRightsEditor.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								virtweb_frontend/src/widgets/tokens/RawRightsEditor.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
			
		||||
import AddIcon from "@mui/icons-material/Add";
 | 
			
		||||
import DeleteIcon from "@mui/icons-material/Delete";
 | 
			
		||||
import {
 | 
			
		||||
  IconButton,
 | 
			
		||||
  Paper,
 | 
			
		||||
  Table,
 | 
			
		||||
  TableBody,
 | 
			
		||||
  TableCell,
 | 
			
		||||
  TableContainer,
 | 
			
		||||
  TableHead,
 | 
			
		||||
  TableRow,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { ServerApi } from "../../api/ServerApi";
 | 
			
		||||
import { APIToken } from "../../api/TokensApi";
 | 
			
		||||
import { SelectInput } from "../forms/SelectInput";
 | 
			
		||||
import { TextInput } from "../forms/TextInput";
 | 
			
		||||
 | 
			
		||||
export function RawRightsEditor(p: {
 | 
			
		||||
  token: APIToken;
 | 
			
		||||
  editable: boolean;
 | 
			
		||||
  onChange?: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const addRule = () => {
 | 
			
		||||
    p.token.rights.push({ path: "/api/", verb: "GET" });
 | 
			
		||||
    p.onChange?.();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const deleteRule = (id: number) => {
 | 
			
		||||
    p.token.rights.splice(id, 1);
 | 
			
		||||
    p.onChange?.();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <TableContainer component={Paper}>
 | 
			
		||||
      <div
 | 
			
		||||
        style={{
 | 
			
		||||
          padding: "10px 10px 0px 10px",
 | 
			
		||||
          display: "flex",
 | 
			
		||||
          justifyContent: "space-between",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <Typography variant="h5">Raw rights</Typography>
 | 
			
		||||
        <div>
 | 
			
		||||
          {p.editable && (
 | 
			
		||||
            <Tooltip title="Add a new right rule">
 | 
			
		||||
              <IconButton onClick={addRule}>
 | 
			
		||||
                <AddIcon />
 | 
			
		||||
              </IconButton>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <Table>
 | 
			
		||||
        <TableHead>
 | 
			
		||||
          <TableRow>
 | 
			
		||||
            <TableCell>Verb</TableCell>
 | 
			
		||||
            <TableCell>URI</TableCell>
 | 
			
		||||
            {p.editable && <TableCell>Actions</TableCell>}
 | 
			
		||||
          </TableRow>
 | 
			
		||||
        </TableHead>
 | 
			
		||||
        <TableBody>
 | 
			
		||||
          {p.token.rights.map((r, num) => (
 | 
			
		||||
            <TableRow key={num} hover>
 | 
			
		||||
              <TableCell style={{ width: "100px" }}>
 | 
			
		||||
                <SelectInput
 | 
			
		||||
                  {...p}
 | 
			
		||||
                  value={r.verb}
 | 
			
		||||
                  onValueChange={(v) => {
 | 
			
		||||
                    r.verb = v as any;
 | 
			
		||||
                    p.onChange?.();
 | 
			
		||||
                  }}
 | 
			
		||||
                  options={[
 | 
			
		||||
                    { value: "GET" },
 | 
			
		||||
                    { value: "POST" },
 | 
			
		||||
                    { value: "PATCH" },
 | 
			
		||||
                    { value: "PUT" },
 | 
			
		||||
                    { value: "DELETE" },
 | 
			
		||||
                  ]}
 | 
			
		||||
                />
 | 
			
		||||
              </TableCell>
 | 
			
		||||
              <TableCell>
 | 
			
		||||
                <TextInput
 | 
			
		||||
                  {...p}
 | 
			
		||||
                  value={r.path}
 | 
			
		||||
                  onValueChange={(v) => {
 | 
			
		||||
                    r.path = v ?? "";
 | 
			
		||||
                    p.onChange?.();
 | 
			
		||||
                  }}
 | 
			
		||||
                  checkValue={(v) => v.startsWith("/api/")}
 | 
			
		||||
                  size={ServerApi.Config.constraints.api_token_right_path_size}
 | 
			
		||||
                />
 | 
			
		||||
              </TableCell>
 | 
			
		||||
              {p.editable && (
 | 
			
		||||
                <TableCell style={{ width: "100px" }}>
 | 
			
		||||
                  <IconButton onClick={() => deleteRule(num)}>
 | 
			
		||||
                    <Tooltip title="Remove the rule">
 | 
			
		||||
                      <DeleteIcon />
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                  </IconButton>
 | 
			
		||||
                </TableCell>
 | 
			
		||||
              )}
 | 
			
		||||
            </TableRow>
 | 
			
		||||
          ))}
 | 
			
		||||
        </TableBody>
 | 
			
		||||
      </Table>
 | 
			
		||||
    </TableContainer>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user