Browse Source

WIP: Refactoring

pull/619/head
philon- 1 year ago
parent
commit
63872396ea
  1. 121
      src/components/PageComponents/Config/Security/Security.tsx
  2. 10
      src/components/PageComponents/Config/Security/securityReducer.tsx
  3. 13
      src/components/PageComponents/Config/Security/types.ts
  4. 11
      src/validation/config/security.ts

121
src/components/PageComponents/Config/Security/Security.tsx

@ -7,7 +7,7 @@ import { create } from "@bufbuild/protobuf";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/core"; import { Protobuf } from "@meshtastic/core";
import { fromByteArray, toByteArray } from "base64-js"; import { fromByteArray, toByteArray } from "base64-js";
import { useEffect, useReducer, useRef } from "react"; import { useReducer } from "react";
import { securityReducer } from "@components/PageComponents/Config/Security/securityReducer.tsx"; import { securityReducer } from "@components/PageComponents/Config/Security/securityReducer.tsx";
export const Security = () => { export const Security = () => {
@ -24,51 +24,39 @@ export const Security = () => {
const [state, dispatch] = useReducer(securityReducer, { const [state, dispatch] = useReducer(securityReducer, {
privateKey: fromByteArray(config.security?.privateKey ?? new Uint8Array(0)), privateKey: fromByteArray(config.security?.privateKey ?? new Uint8Array(0)),
privateKeyVisible: false, privateKeyVisible: false,
adminKeyVisible: false, adminKeyVisible: [false, false, false],
adminKey2Visible: false,
adminKey3Visible: false,
privateKeyBitCount: config.security?.privateKey?.length ?? 32, privateKeyBitCount: config.security?.privateKey?.length ?? 32,
publicKey: fromByteArray(config.security?.publicKey ?? new Uint8Array(0)), publicKey: fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
adminKey1: fromByteArray( adminKey: [
config.security?.adminKey?.at(0) ?? new Uint8Array(0), fromByteArray(config.security?.adminKey?.at(0) ?? new Uint8Array(0)),
), fromByteArray(config.security?.adminKey?.at(1) ?? new Uint8Array(0)),
adminKey2: fromByteArray( fromByteArray(config.security?.adminKey?.at(2) ?? new Uint8Array(0)),
config.security?.adminKey?.at(1) ?? new Uint8Array(0), ],
),
adminKey3: fromByteArray(
config.security?.adminKey?.at(2) ?? new Uint8Array(0),
),
privateKeyDialogOpen: false, privateKeyDialogOpen: false,
}); });
const adminKeysRef = useRef(state);
useEffect(() => {
adminKeysRef.current = state;
}, [state]);
const validateKey = ( const validateKey = (
input: string, input: string,
count: number, count: number,
fieldName: "privateKey" | "adminKey1" | "adminKey2" | "adminKey3", fieldName: "privateKey" | "adminKey",
fieldIndex?: number,
) => { ) => {
const fieldNameKey = fieldName + (fieldIndex ?? "");
try { try {
removeError(fieldName); removeError(fieldNameKey);
if (fieldName === "privateKey" && input === "") { if (fieldName === "privateKey" && input === "") {
addError(fieldName, "Private Key is required"); addError(fieldNameKey, "Private Key is required");
return; return;
} }
if ( if (fieldName === "adminKey" && input === "") {
(fieldName === "adminKey1" || fieldName === "adminKey2" ||
fieldName === "adminKey3") && input === ""
) {
return; return;
} }
if (input.length % 4 !== 0) { if (input.length % 4 !== 0) {
addError( addError(
fieldName, fieldNameKey,
`${ `${
fieldName === "privateKey" ? "Private" : "Admin" fieldName === "privateKey" ? "Private" : "Admin"
} Key is required to be a 256 bit pre-shared key (PSK)`, } Key is required to be a 256 bit pre-shared key (PSK)`,
@ -78,13 +66,13 @@ export const Security = () => {
const decoded = toByteArray(input); const decoded = toByteArray(input);
if (decoded.length !== count) { if (decoded.length !== count) {
addError(fieldName, `Please enter a valid ${count * 8} bit PSK`); addError(fieldNameKey, `Please enter a valid ${count * 8} bit PSK`);
return; return;
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
addError( addError(
fieldName, fieldNameKey,
`Invalid ${ `Invalid ${
fieldName === "privateKey" ? "Private" : "Admin" fieldName === "privateKey" ? "Private" : "Admin"
} Key format`, } Key format`,
@ -97,8 +85,6 @@ export const Security = () => {
return; return;
} }
const current = adminKeysRef.current;
setWorkingConfig( setWorkingConfig(
create(Protobuf.Config.ConfigSchema, { create(Protobuf.Config.ConfigSchema, {
payloadVariant: { payloadVariant: {
@ -106,12 +92,12 @@ export const Security = () => {
value: { value: {
...data, ...data,
adminKey: [ adminKey: [
toByteArray(current.adminKey1), toByteArray(state.adminKey[0]),
toByteArray(current.adminKey2), toByteArray(state.adminKey[1]),
toByteArray(current.adminKey3), toByteArray(state.adminKey[2]),
], ],
privateKey: toByteArray(current.privateKey), privateKey: toByteArray(state.privateKey),
publicKey: toByteArray(current.publicKey), publicKey: toByteArray(state.publicKey),
}, },
}, },
}), }),
@ -149,14 +135,21 @@ export const Security = () => {
dispatch({ type: "SET_PUBLIC_KEY", payload: fromByteArray(publicKey) }); dispatch({ type: "SET_PUBLIC_KEY", payload: fromByteArray(publicKey) });
}; };
const adminKeyInputChangeEvent = (e: React.ChangeEvent<HTMLInputElement>) => { const adminKeyInputChangeEvent = (
const psk = e.currentTarget?.value; e: React.ChangeEvent<HTMLInputElement>,
const id = e.currentTarget?.id.match(/^adminKey(\d+)(?=Input$)/)?.[1]; fieldIndex?: number,
) => {
if (fieldIndex === undefined) return;
const psk = e.target.value;
if (!(id === "1" || id === "2" || id === "3")) return; const payload = [
fieldIndex === 0 ? psk : state.adminKey[0],
fieldIndex === 1 ? psk : state.adminKey[1],
fieldIndex === 2 ? psk : state.adminKey[2],
] satisfies [string, string, string];
dispatch({ type: `SET_ADMIN${id}_KEY`, payload: psk }); dispatch({ type: "SET_ADMIN_KEY", payload: payload });
validateKey(psk, state.privateKeyBitCount, `adminKey${id}`); validateKey(psk, state.privateKeyBitCount, "adminKey", fieldIndex);
}; };
const privateKeySelectChangeEvent = (e: string) => { const privateKeySelectChangeEvent = (e: string) => {
@ -173,9 +166,7 @@ export const Security = () => {
defaultValues={{ defaultValues={{
...config.security, ...config.security,
...{ ...{
adminKey1: state.adminKey1, adminKey: state.adminKey,
adminKey2: state.adminKey2,
adminKey3: state.adminKey3,
privateKey: state.privateKey, privateKey: state.privateKey,
publicKey: state.publicKey, publicKey: state.publicKey,
adminChannelEnabled: config.security?.adminChannelEnabled ?? false, adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
@ -245,75 +236,75 @@ export const Security = () => {
fields: [ fields: [
{ {
type: "passwordGenerator", type: "passwordGenerator",
name: "adminKey1", name: "adminKey.0",
id: "adminKey1Input", id: "adminKey0Input",
label: "Primary Admin Key", label: "Primary Admin Key",
description: description:
"The primary public key authorized to send admin messages to this node", "The primary public key authorized to send admin messages to this node",
validationText: hasFieldError("adminKey1") validationText: hasFieldError("adminKey0")
? getErrorMessage("adminKey1") ? getErrorMessage("adminKey0")
: "", : "",
inputChange: adminKeyInputChangeEvent, inputChange: (e) => adminKeyInputChangeEvent(e, 0),
selectChange: () => {}, selectChange: () => {},
bits: [{ text: "256 bit", value: "32", key: "bit256" }], bits: [{ text: "256 bit", value: "32", key: "bit256" }],
devicePSKBitCount: state.privateKeyBitCount, devicePSKBitCount: state.privateKeyBitCount,
hide: !state.adminKeyVisible, hide: !state.adminKeyVisible[0],
actionButtons: [], actionButtons: [],
disabledBy: [ disabledBy: [
{ fieldName: "adminChannelEnabled", invert: true }, { fieldName: "adminChannelEnabled", invert: true },
], ],
properties: { properties: {
value: state.adminKey1, value: state.adminKey[0],
showCopyButton: true, showCopyButton: true,
showPasswordToggle: true, showPasswordToggle: true,
}, },
}, },
{ {
type: "passwordGenerator", type: "passwordGenerator",
name: "adminKey2", name: "adminKey.1",
id: "adminKey2Input", id: "adminKey1Input",
label: "Secondary Admin Key", label: "Secondary Admin Key",
description: description:
"The secondary public key authorized to send admin messages to this node", "The secondary public key authorized to send admin messages to this node",
validationText: hasFieldError("adminKey2") validationText: hasFieldError("adminKey1")
? getErrorMessage("adminKey2") ? getErrorMessage("adminKey1")
: "", : "",
inputChange: adminKeyInputChangeEvent, inputChange: (e) => adminKeyInputChangeEvent(e, 1),
selectChange: () => {}, selectChange: () => {},
bits: [{ text: "256 bit", value: "32", key: "bit256" }], bits: [{ text: "256 bit", value: "32", key: "bit256" }],
devicePSKBitCount: state.privateKeyBitCount, devicePSKBitCount: state.privateKeyBitCount,
hide: !state.adminKey2Visible, hide: !state.adminKeyVisible[1],
actionButtons: [], actionButtons: [],
disabledBy: [ disabledBy: [
{ fieldName: "adminChannelEnabled", invert: true }, { fieldName: "adminChannelEnabled", invert: true },
], ],
properties: { properties: {
value: state.adminKey2, value: state.adminKey[1],
showCopyButton: true, showCopyButton: true,
showPasswordToggle: true, showPasswordToggle: true,
}, },
}, },
{ {
type: "passwordGenerator", type: "passwordGenerator",
name: "adminKey3", name: "adminKey.2",
id: "adminKey3Input", id: "adminKey2Input",
label: "Tertiary Admin Key", label: "Tertiary Admin Key",
description: description:
"The tertiary public key authorized to send admin messages to this node", "The tertiary public key authorized to send admin messages to this node",
validationText: hasFieldError("adminKey3") validationText: hasFieldError("adminKey2")
? getErrorMessage("adminKey3") ? getErrorMessage("adminKey2")
: "", : "",
inputChange: adminKeyInputChangeEvent, inputChange: (e) => adminKeyInputChangeEvent(e, 2),
selectChange: () => {}, selectChange: () => {},
bits: [{ text: "256 bit", value: "32", key: "bit256" }], bits: [{ text: "256 bit", value: "32", key: "bit256" }],
devicePSKBitCount: state.privateKeyBitCount, devicePSKBitCount: state.privateKeyBitCount,
hide: !state.adminKey3Visible, hide: !state.adminKeyVisible[2],
actionButtons: [], actionButtons: [],
disabledBy: [ disabledBy: [
{ fieldName: "adminChannelEnabled", invert: true }, { fieldName: "adminChannelEnabled", invert: true },
], ],
properties: { properties: {
value: state.adminKey3, value: state.adminKey[2],
showCopyButton: true, showCopyButton: true,
showPasswordToggle: true, showPasswordToggle: true,
}, },

10
src/components/PageComponents/Config/Security/securityReducer.tsx

@ -9,18 +9,12 @@ export function securityReducer(
return { ...state, privateKey: action.payload }; return { ...state, privateKey: action.payload };
case "TOGGLE_PRIVATE_KEY_VISIBILITY": case "TOGGLE_PRIVATE_KEY_VISIBILITY":
return { ...state, privateKeyVisible: !state.privateKeyVisible }; return { ...state, privateKeyVisible: !state.privateKeyVisible };
case "TOGGLE_ADMIN_KEY_VISIBILITY":
return { ...state, adminKeyVisible: !state.adminKeyVisible };
case "SET_PRIVATE_KEY_BIT_COUNT": case "SET_PRIVATE_KEY_BIT_COUNT":
return { ...state, privateKeyBitCount: action.payload }; return { ...state, privateKeyBitCount: action.payload };
case "SET_PUBLIC_KEY": case "SET_PUBLIC_KEY":
return { ...state, publicKey: action.payload }; return { ...state, publicKey: action.payload };
case "SET_ADMIN1_KEY": case "SET_ADMIN_KEY":
return { ...state, adminKey1: action.payload }; return { ...state, adminKey: action.payload };
case "SET_ADMIN2_KEY":
return { ...state, adminKey2: action.payload };
case "SET_ADMIN3_KEY":
return { ...state, adminKey3: action.payload };
case "SHOW_PRIVATE_KEY_DIALOG": case "SHOW_PRIVATE_KEY_DIALOG":
return { ...state, privateKeyDialogOpen: action.payload }; return { ...state, privateKeyDialogOpen: action.payload };
case "REGENERATE_PRIV_PUB_KEY": case "REGENERATE_PRIV_PUB_KEY":

13
src/components/PageComponents/Config/Security/types.ts

@ -1,26 +1,19 @@
export interface SecurityState { export interface SecurityState {
privateKey: string; privateKey: string;
privateKeyVisible: boolean; privateKeyVisible: boolean;
adminKeyVisible: boolean; adminKeyVisible: [boolean, boolean, boolean];
adminKey2Visible: boolean;
adminKey3Visible: boolean;
privateKeyBitCount: number; privateKeyBitCount: number;
publicKey: string; publicKey: string;
adminKey1: string; adminKey: [string, string, string];
adminKey2: string;
adminKey3: string;
privateKeyDialogOpen: boolean; privateKeyDialogOpen: boolean;
} }
export type SecurityAction = export type SecurityAction =
| { type: "SET_PRIVATE_KEY"; payload: string } | { type: "SET_PRIVATE_KEY"; payload: string }
| { type: "TOGGLE_PRIVATE_KEY_VISIBILITY" } | { type: "TOGGLE_PRIVATE_KEY_VISIBILITY" }
| { type: "TOGGLE_ADMIN_KEY_VISIBILITY" }
| { type: "SET_PRIVATE_KEY_BIT_COUNT"; payload: number } | { type: "SET_PRIVATE_KEY_BIT_COUNT"; payload: number }
| { type: "SET_PUBLIC_KEY"; payload: string } | { type: "SET_PUBLIC_KEY"; payload: string }
| { type: "SET_ADMIN1_KEY"; payload: string } | { type: "SET_ADMIN_KEY"; payload: [string, string, string] }
| { type: "SET_ADMIN2_KEY"; payload: string }
| { type: "SET_ADMIN3_KEY"; payload: string }
| { type: "SHOW_PRIVATE_KEY_DIALOG"; payload: boolean } | { type: "SHOW_PRIVATE_KEY_DIALOG"; payload: boolean }
| { | {
type: "REGENERATE_PRIV_PUB_KEY"; type: "REGENERATE_PRIV_PUB_KEY";

11
src/validation/config/security.ts

@ -7,9 +7,6 @@ export class SecurityValidation implements
Protobuf.Config.Config_SecurityConfig, Protobuf.Config.Config_SecurityConfig,
| keyof Message | keyof Message
| "adminKey" | "adminKey"
| "adminKey1"
| "adminKey2"
| "adminKey3"
| "privateKey" | "privateKey"
| "publicKey" | "publicKey"
> { > {
@ -17,13 +14,7 @@ export class SecurityValidation implements
adminChannelEnabled: boolean; adminChannelEnabled: boolean;
@IsString() @IsString()
adminKey1: string; adminKey: [string, string, string];
@IsString()
adminKey2: string;
@IsString()
adminKey3: string;
@IsBoolean() @IsBoolean()
bluetoothLoggingEnabled: boolean; bluetoothLoggingEnabled: boolean;

Loading…
Cancel
Save