Peer certificate check when adding

This commit is contained in:
2025-04-25 10:48:55 +02:00
parent f43c6db4f0
commit bed55162d7
21 changed files with 160 additions and 111 deletions

View File

@@ -2,6 +2,7 @@ import { TAvailablePeerInfoTo } from "./api/dto";
import { useFetcher } from "react-router-dom";
import "./PeerAvailableCard.scss";
import { hashCert } from "./hash";
export interface TPeerAvailableCardProps {
peerInfo: TAvailablePeerInfoTo;
@@ -16,6 +17,9 @@ export function PeerAvailableCard({ peerInfo }: TPeerAvailableCardProps) {
<span>UUID: </span>
<span>{peerInfo.uuid}</span>
</div>
<div>
<span>Cert: {hashCert(peerInfo.cert)}</span>
</div>
</div>
<fetcher.Form
className="actions"
@@ -25,6 +29,7 @@ export function PeerAvailableCard({ peerInfo }: TPeerAvailableCardProps) {
<button type="submit">connect</button>
<input name="intent" hidden={true} defaultValue={"add_peer"} />
<input name="uuid" hidden={true} defaultValue={peerInfo.uuid} />
<input name="cert" hidden={true} defaultValue={peerInfo.cert} />
</fetcher.Form>
</div>
);

View File

@@ -4,6 +4,7 @@ import "./PeerKnownCard.scss";
import { useFetcher, useLoaderData } from "react-router-dom";
import { LoaderToType } from "./commonPlumbing";
import { peerStateLoader } from "./PeerStatePlumbing";
import { hashCert } from "./hash";
export interface TPeerKnownCardProps {
peerInfo: TKnownPeerInfoTo;
@@ -32,6 +33,9 @@ export function PeerKnownCard({ peerInfo }: TPeerKnownCardProps) {
: "not connected"}
</span>
</div>
<div>
<span>Cert: {hashCert(peerInfo.cert)}</span>
</div>
</div>
<div>
<fetcher.Form method="put" action={"/home/peers"}>

View File

@@ -4,12 +4,13 @@ import { LoaderToType } from "./commonPlumbing";
import { peerStateLoader } from "./PeerStatePlumbing";
import { PeerAvailableCard } from "./PeerAvailableCard";
import { PeerKnownCard } from "./PeerKnownCard";
import { hashCert } from "./hash";
export function PeerState() {
const loaderData = useLoaderData() as LoaderToType<typeof peerStateLoader>;
const knownPeers = loaderData.knownPeers.peers
?.filter((p) => p.uuid !== loaderData.knownPeers.selfUuid)
const knownPeers = loaderData.knownPeers
?.filter((p) => p.uuid !== loaderData.selfInfo.selfUuid)
?.map((p) => <PeerKnownCard peerInfo={p} key={p.uuid} />);
const availablePeers = loaderData.availablePeers.map((p) => (
@@ -18,7 +19,8 @@ export function PeerState() {
return (
<div id={"PeerState"}>
<div>Self UUID: {loaderData.knownPeers.selfUuid}</div>
<div>UUID: {loaderData.selfInfo.selfUuid}</div>
<div>Certificate: {hashCert(loaderData.selfInfo.cert)} </div>
<div>
<div>Known peers</div>
<div>{knownPeers}</div>

View File

@@ -2,6 +2,7 @@ import {
getAvailablePeers,
getKnownPeers,
getPeerAddresses,
getSelfInfo,
putKnownPeer,
putPeerAddress,
removeKnownPeer,
@@ -10,6 +11,7 @@ import { ActionFunctionArgs } from "react-router-dom";
export async function peerStateLoader() {
return {
selfInfo: await getSelfInfo(),
availablePeers: await getAvailablePeers(),
knownPeers: await getKnownPeers(),
peerAddresses: await getPeerAddresses(),
@@ -26,7 +28,10 @@ export async function peerStateAction({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const intent = formData.get("intent") as PeerStateActionType;
if (intent === "add_peer") {
return await putKnownPeer(formData.get("uuid") as string);
return await putKnownPeer(
formData.get("uuid") as string,
formData.get("cert") as string,
);
} else if (intent === "remove_peer") {
return await removeKnownPeer(formData.get("uuid") as string);
} else if (intent === "save_addr") {

View File

@@ -4,15 +4,26 @@ import {
KnownPeersToResp,
NoContentToResp,
PeerAddressInfoToResp,
SelfInfoToResp,
TAvailablePeerInfoArrTo,
TAvailablePeerInfoToResp,
TKnownPeerInfoArrTo, TKnownPeersTo,
TKnownPeersTo,
TKnownPeersToResp,
TNoContentToResp,
TPeerAddressInfoArrTo,
TPeerAddressInfoToResp,
TSelfInfoTo,
TSelfInfoToResp,
} from "./dto";
export async function getSelfInfo(): Promise<TSelfInfoTo> {
return fetchJSON_throws<TSelfInfoToResp, typeof SelfInfoToResp>(
"/peer-info/self",
"GET",
SelfInfoToResp,
);
}
export async function getAvailablePeers(): Promise<TAvailablePeerInfoArrTo> {
return fetchJSON_throws<
TAvailablePeerInfoToResp,
@@ -28,16 +39,27 @@ export async function getKnownPeers(): Promise<TKnownPeersTo> {
);
}
export async function putKnownPeer(uuid: string): Promise<TNoContentToResp> {
return fetchJSON("/peers-manage/known-peers", "PUT", NoContentToResp, {
uuid,
});
export async function putKnownPeer(
uuid: string,
cert: string,
): Promise<TNoContentToResp> {
return fetchJSON(
`/peers-manage/known-peers/${uuid}`,
"PUT",
NoContentToResp,
{
cert,
},
);
}
export async function removeKnownPeer(uuid: string): Promise<TNoContentToResp> {
return fetchJSON("/peers-manage/known-peers", "DELETE", NoContentToResp, {
uuid,
});
return fetchJSON(
`/peers-manage/known-peers/${uuid}`,
"DELETE",
NoContentToResp,
{},
);
}
export async function getPeerAddresses(): Promise<TPeerAddressInfoArrTo> {

View File

@@ -36,13 +36,28 @@ export type TTokenTo = z.infer<typeof TokenTo>;
export const TokenToResp = CreateAPIResponse(TokenTo);
export type TTokenToResp = z.infer<typeof TokenToResp>;
// AvailablePeerInfo
export const AvailablePeerInfoTo = z.object({
// SelfInfo
export const SelfInfoTo = z.object({
selfUuid: z.string(),
cert: z.string(),
});
export type TSelfInfoTo = z.infer<typeof SelfInfoTo>;
export const SelfInfoToResp = CreateAPIResponse(SelfInfoTo);
export type TSelfInfoToResp = z.infer<typeof SelfInfoToResp>;
// PeerInfo
export const PeerInfoTo = z.object({
uuid: z.string(),
knownAddress: z.string().optional(),
// addr: z.string(),
// port: z.number(),
cert: z.string(),
});
export type TPeerInfoTo = z.infer<typeof PeerInfoTo>;
// AvailablePeerInfo
export const AvailablePeerInfoTo = PeerInfoTo;
export type TAvailablePeerInfoTo = z.infer<typeof AvailablePeerInfoTo>;
export const AvailablePeerInfoArrTo = z.array(AvailablePeerInfoTo);
@@ -54,19 +69,13 @@ export const AvailablePeerInfoToResp = CreateAPIResponse(
export type TAvailablePeerInfoToResp = z.infer<typeof AvailablePeerInfoToResp>;
// KnownPeerInfo
export const KnownPeerInfoTo = z.object({
uuid: z.string(),
knownAddress: z.string().optional(),
});
export const KnownPeerInfoTo = PeerInfoTo;
export type TKnownPeerInfoTo = z.infer<typeof KnownPeerInfoTo>;
export const KnownPeerInfoArrTo = z.array(KnownPeerInfoTo);
export type TKnownPeerInfoArrTo = z.infer<typeof KnownPeerInfoArrTo>;
export const KnownPeersTo = z.object({
selfUuid: z.string(),
peers: KnownPeerInfoArrTo,
});
export const KnownPeersTo = KnownPeerInfoArrTo;
export type TKnownPeersTo = z.infer<typeof KnownPeersTo>;
export const KnownPeersToResp = CreateAPIResponse(KnownPeersTo);
@@ -86,9 +95,9 @@ export const PeerAddressInfoToResp = CreateAPIResponse(PeerAddressInfoArrTo);
export type TPeerAddressInfoToResp = z.infer<typeof PeerAddressInfoToResp>;
// KnownPeerPut
export const KnownPeerPutTo = z.object({ uuid: z.string() });
export const KnownPeerPutTo = z.object({ cert: z.string() });
export type TKnownPeerPutTo = z.infer<typeof KnownPeerPutTo>;
// KnownPeerDelete
export const KnownPeerDeleteTo = z.object({ uuid: z.string() });
export const KnownPeerDeleteTo = NoContentTo;
export type TKnownPeerDeleteTo = z.infer<typeof KnownPeerDeleteTo>;

7
webui/src/hash.ts Normal file
View File

@@ -0,0 +1,7 @@
export async function hashCert(cert: string) {
const hash = await crypto.subtle.digest(
"SHA-1",
new TextEncoder().encode(cert),
);
return btoa(String.fromCharCode(...new Uint8Array(hash)));
}