swarm-ui: show and toggle an agent's unrestricted internet flag, add it to the create dialog
This commit is contained in:
parent
24beb167d8
commit
10508895c4
4 changed files with 136 additions and 2 deletions
|
|
@ -23,12 +23,18 @@ import {
|
|||
requestPermissions,
|
||||
togglePermission,
|
||||
} from "../../pages/CreateAgentPermissions.js";
|
||||
import { editorSelection, grantSummary } from "./permissions.js";
|
||||
import {
|
||||
editorSelection,
|
||||
grantSummary,
|
||||
internetBody,
|
||||
internetSummary,
|
||||
} from "./permissions.js";
|
||||
import "./AgentLimits.css";
|
||||
|
||||
// The stored grant as the route carries it; the field is absent when unset.
|
||||
interface Grant {
|
||||
permissions?: string[];
|
||||
unrestricted_internet?: boolean;
|
||||
}
|
||||
|
||||
// Mirrors swarm-controller's `Catalogue` (`GET /api/permissions`).
|
||||
|
|
@ -47,6 +53,7 @@ export function AgentPermissions({ agent }: { agent: string }) {
|
|||
// Bumped after a save or a clear, so the grant is fetched again.
|
||||
const [reload, setReload] = useState(0);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [flagBusy, setFlagBusy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
|
@ -68,6 +75,28 @@ export function AgentPermissions({ agent }: { agent: string }) {
|
|||
};
|
||||
}, [agent, reload]);
|
||||
|
||||
// Flips the internet flag, sending the stored list with it.
|
||||
async function setInternet(g: Grant, unrestricted: boolean) {
|
||||
if (flagBusy) return;
|
||||
setFlagBusy(true);
|
||||
try {
|
||||
const r = await fetch(permissionsUrl(agent), {
|
||||
method: "PUT",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(internetBody(g.permissions, unrestricted)),
|
||||
});
|
||||
if (!r.ok) {
|
||||
setError(await readApiError(r));
|
||||
return;
|
||||
}
|
||||
setReload((n) => n + 1);
|
||||
} catch (e: unknown) {
|
||||
setError({ detail: String(e) });
|
||||
} finally {
|
||||
setFlagBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
// A grant already loaded stays under a failed refetch, so a save that
|
||||
// worked is not hidden by a read that did not.
|
||||
const errorPanel = error ? (
|
||||
|
|
@ -99,6 +128,22 @@ export function AgentPermissions({ agent }: { agent: string }) {
|
|||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
<Badge
|
||||
label="internet"
|
||||
value={internetSummary(grant.unrestricted_internet)}
|
||||
/>
|
||||
<Badge
|
||||
variant="quiet"
|
||||
value={
|
||||
grant.unrestricted_internet === false
|
||||
? "allow unrestricted"
|
||||
: "restrict"
|
||||
}
|
||||
title={`set whether ${agent}'s traffic may leave the host unfiltered`}
|
||||
onClick={() =>
|
||||
void setInternet(grant, grant.unrestricted_internet === false)
|
||||
}
|
||||
/>
|
||||
<Badge
|
||||
variant="quiet"
|
||||
value="edit"
|
||||
|
|
|
|||
|
|
@ -21,3 +21,27 @@ export function grantSummary(stored: string[] | undefined): string {
|
|||
if (stored.length === 0) return "none";
|
||||
return `${stored.length} granted`;
|
||||
}
|
||||
|
||||
/**
|
||||
* How the panel reads the stored `unrestricted_internet`. An absent value
|
||||
* counts as on, and says "legacy" because the swarm never set it.
|
||||
*/
|
||||
export function internetSummary(stored: boolean | undefined): string {
|
||||
if (stored === undefined) return "unrestricted (legacy)";
|
||||
return stored ? "unrestricted" : "restricted";
|
||||
}
|
||||
|
||||
/**
|
||||
* The `PUT` body that sets the internet flag. A `PUT` without `permissions`
|
||||
* resets a stored list to the default, so the stored list goes along; an
|
||||
* agent with none stored sends the flag alone, which leaves it unset.
|
||||
*/
|
||||
export function internetBody(
|
||||
stored: string[] | undefined,
|
||||
unrestricted: boolean,
|
||||
): { permissions?: string[]; unrestricted_internet: boolean } {
|
||||
return {
|
||||
...(stored === undefined ? {} : { permissions: stored }),
|
||||
unrestricted_internet: unrestricted,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import { FormField } from "../ui/form-field/FormField.js";
|
|||
import { MultiselectFilterOptions } from "../ui/multiselect-filter/MultiselectFilter.js";
|
||||
import {
|
||||
initialSelection,
|
||||
requestInternet,
|
||||
requestPermissions,
|
||||
togglePermission,
|
||||
} from "./CreateAgentPermissions.js";
|
||||
|
|
@ -132,6 +133,8 @@ export function CreateAgentForm({
|
|||
null,
|
||||
);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
// Unticked: the server writes `false`, so the agent starts restricted.
|
||||
const [internet, setInternet] = useState(false);
|
||||
// `null` until `/api/archetypes` answers; `archetype` is the picked one's
|
||||
// `archetypeKey`, `""` for none.
|
||||
const [archetypes, setArchetypes] = useState<ArchetypeList | null>(null);
|
||||
|
|
@ -231,6 +234,7 @@ export function CreateAgentForm({
|
|||
permissions: catalogue
|
||||
? requestPermissions(selected, catalogue.default)
|
||||
: undefined,
|
||||
...requestInternet(internet),
|
||||
archetype: requestArchetype(picked),
|
||||
...agentsMdBody(agentsMd),
|
||||
}),
|
||||
|
|
@ -434,6 +438,14 @@ export function CreateAgentForm({
|
|||
)}
|
||||
</div>
|
||||
</FormField>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={internet}
|
||||
onChange={() => setInternet((v) => !v)}
|
||||
/>{" "}
|
||||
Unrestricted internet
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{!done && step === "resources" && (
|
||||
|
|
|
|||
|
|
@ -38,3 +38,13 @@ export function initialSelection(
|
|||
...extra.filter((p) => all.includes(p) && !defaults.includes(p)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The create request's internet field: `unrestricted_internet: true` while
|
||||
* the box is ticked, nothing otherwise, as the server writes `false` for it.
|
||||
*/
|
||||
export function requestInternet(ticked: boolean): {
|
||||
unrestricted_internet?: true;
|
||||
} {
|
||||
return ticked ? { unrestricted_internet: true } : {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue