From 1ae879342a42dfb346c4c14c31de1d2f1fdd1b51 Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sun, 28 Jul 2024 15:09:11 -0400 Subject: [PATCH 01/10] added password generator --- src/components/Form/DynamicFormField.tsx | 11 +- src/components/Form/FormPasswordGenerator.tsx | 39 ++++++ src/components/PageComponents/Channel.tsx | 4 +- src/components/UI/Button.tsx | 2 + src/components/UI/Generator.tsx | 111 ++++++++++++++++++ 5 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 src/components/Form/FormPasswordGenerator.tsx create mode 100644 src/components/UI/Generator.tsx diff --git a/src/components/Form/DynamicFormField.tsx b/src/components/Form/DynamicFormField.tsx index fc2f7575..6f517e95 100644 --- a/src/components/Form/DynamicFormField.tsx +++ b/src/components/Form/DynamicFormField.tsx @@ -10,12 +10,17 @@ import { type ToggleFieldProps, ToggleInput, } from "@components/Form/FormToggle.js"; +import { + type PasswordGeneratorProps, + PasswordGenerator, +} from "@components/Form/FormPasswordGenerator.js"; import type { Control, FieldValues } from "react-hook-form"; export type FieldProps = | InputFieldProps | SelectFieldProps - | ToggleFieldProps; + | ToggleFieldProps + | PasswordGeneratorProps; export interface DynamicFormFieldProps { field: FieldProps; @@ -44,6 +49,10 @@ export function DynamicFormField({ return ( ); + case "passwordGenerator": + return ( + + ) case "multiSelect": return
tmp
; } diff --git a/src/components/Form/FormPasswordGenerator.tsx b/src/components/Form/FormPasswordGenerator.tsx new file mode 100644 index 00000000..eb30dc1a --- /dev/null +++ b/src/components/Form/FormPasswordGenerator.tsx @@ -0,0 +1,39 @@ +import type { + BaseFormBuilderProps, + GenericFormElementProps, + } from "@components/Form/DynamicForm.js"; + import { Generator } from "@components/UI/Generator.js"; + import { useState } from "react"; + import { Controller, type FieldValues } from "react-hook-form"; + + export interface PasswordGeneratorProps extends BaseFormBuilderProps { + type: "passwordGenerator"; + } + + export function PasswordGenerator({ + control, + field, + }: GenericFormElementProps>) { + const [password, createPassword] = useState(""); + + const generate = () => { + let generatedPass = "VHl1OTVpY7TAly0jGF0X2A=="; + return generatedPass + } + + return ( + ( + + )} + /> + ); + } + \ No newline at end of file diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index 464d04e7..9b5bb9f2 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -76,10 +76,10 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }, }, { - type: "password", + type: "passwordGenerator", name: "settings.psk", label: "pre-Shared Key", - description: "16, or 32 bytes", + description: "256, 128, or 8 bit PSKs allowed", properties: { // act }, diff --git a/src/components/UI/Button.tsx b/src/components/UI/Button.tsx index cea50fea..f903f25e 100644 --- a/src/components/UI/Button.tsx +++ b/src/components/UI/Button.tsx @@ -12,6 +12,8 @@ const buttonVariants = cva( "bg-slate-900 text-white hover:bg-slate-700 dark:bg-slate-50 dark:text-slate-900", destructive: "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600", + success: + "bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600", outline: "bg-transparent border border-slate-200 hover:bg-slate-100 dark:border-slate-700 dark:text-slate-100", subtle: diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx new file mode 100644 index 00000000..64b78bee --- /dev/null +++ b/src/components/UI/Generator.tsx @@ -0,0 +1,111 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "@core/utils/cn.js"; +import { Input } from "@components/UI/Input.js"; +import { Button } from "@components/UI/Button.js"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@components/UI/Select.js"; +import { useState } from "react"; + +const generatorVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2", + { + variants: { + variant: { + default: + "", + destructive: + "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600", + success: + "bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600", + outline: + "bg-transparent border border-slate-200 hover:bg-slate-100 dark:border-slate-700 dark:text-slate-100", + subtle: + "bg-slate-100 text-slate-900 hover:bg-slate-200 dark:bg-slate-700 dark:text-slate-100", + ghost: + "bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent", + link: "bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent", + }, + size: { + default: "h-10 py-2 px-4", + sm: "h-9 px-2 rounded-md", + lg: "h-11 px-8 rounded-md", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface GeneratorProps + extends React.BaseHTMLAttributes, + VariantProps + { + passwordValue?: string; + textValue?: string; + } + +const Generator = React.forwardRef( + ({ passwordValue, textValue, className, variant, size, ...props }, ref) => { + const [pass, setPass] = useState(""); + const [bitCount, setBits] = useState("bit256"); + + const generate = () => { + let generated = "thisisapass"; + if (bitCount == "bit8") { + generated = "8bitpassword" + } + if (bitCount == "bit128") { + generated = "128bitpassword" + } + if (bitCount == "bit256") { + generated = "256bitpassword" + } + return generated; + }; + + return ( + <> + + + + + ); + }, +); +Generator.displayName = "Button"; + +export { Generator, generatorVariants }; From 38b7e600b107e953fde342ad42cf2636f96f915e Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sun, 28 Jul 2024 15:57:17 -0400 Subject: [PATCH 02/10] key generation --- package.json | 1 + pnpm-lock.yaml | 17 +++++++++++++++++ src/components/UI/Generator.tsx | 7 ++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b1b7326f..e56bdaed 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "crypto-random-string": "^5.0.0", "immer": "^10.1.1", "lucide-react": "^0.363.0", "mapbox-gl": "npm:empty-npm-package@^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b94369f4..938ea82e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: cmdk: specifier: ^1.0.0 version: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + crypto-random-string: + specifier: ^5.0.0 + version: 5.0.0 immer: specifier: ^10.1.1 version: 10.1.1 @@ -1901,6 +1904,10 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-random-string@5.0.0: + resolution: {integrity: sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==} + engines: {node: '>=14.16'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2929,6 +2936,10 @@ packages: turf-jsts@1.2.3: resolution: {integrity: sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA==} + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + typescript@5.5.2: resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} engines: {node: '>=14.17'} @@ -5293,6 +5304,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-random-string@5.0.0: + dependencies: + type-fest: 2.19.0 + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -6365,6 +6380,8 @@ snapshots: turf-jsts@1.2.3: {} + type-fest@2.19.0: {} + typescript@5.5.2: {} typewise-core@1.2.0: {} diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index 64b78bee..857ec51d 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -12,6 +12,7 @@ import { SelectValue, } from "@components/UI/Select.js"; import { useState } from "react"; +import cryptoRandomString from 'crypto-random-string'; const generatorVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2", @@ -61,13 +62,13 @@ const Generator = React.forwardRef( const generate = () => { let generated = "thisisapass"; if (bitCount == "bit8") { - generated = "8bitpassword" + generated = btoa(cryptoRandomString({length: 1, type: 'alphanumeric'})); } if (bitCount == "bit128") { - generated = "128bitpassword" + generated = btoa(cryptoRandomString({length: 16, type: 'alphanumeric'})); } if (bitCount == "bit256") { - generated = "256bitpassword" + generated = btoa(cryptoRandomString({length: 32, type: 'alphanumeric'})); } return generated; }; From 9c6aff534a83f223c5ea96a87f823506f721ba1c Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sun, 28 Jul 2024 16:43:41 -0400 Subject: [PATCH 03/10] a bit of cleanup --- src/components/Form/FormPasswordGenerator.tsx | 7 ----- src/components/PageComponents/Channel.tsx | 3 +- src/components/UI/Generator.tsx | 29 +++++-------------- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/components/Form/FormPasswordGenerator.tsx b/src/components/Form/FormPasswordGenerator.tsx index eb30dc1a..a53e7d96 100644 --- a/src/components/Form/FormPasswordGenerator.tsx +++ b/src/components/Form/FormPasswordGenerator.tsx @@ -14,13 +14,6 @@ import type { control, field, }: GenericFormElementProps>) { - const [password, createPassword] = useState(""); - - const generate = () => { - let generatedPass = "VHl1OTVpY7TAly0jGF0X2A=="; - return generatedPass - } - return ( { label: "pre-Shared Key", description: "256, 128, or 8 bit PSKs allowed", properties: { - // act + passwordValue: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)), + devicePSKBitCount: channel?.settings?.psk.length }, }, { diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index 857ec51d..ae42ed3f 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -50,28 +50,15 @@ export interface GeneratorProps extends React.BaseHTMLAttributes, VariantProps { + devicePSKBitCount?: number; passwordValue?: string; textValue?: string; } const Generator = React.forwardRef( - ({ passwordValue, textValue, className, variant, size, ...props }, ref) => { - const [pass, setPass] = useState(""); - const [bitCount, setBits] = useState("bit256"); - - const generate = () => { - let generated = "thisisapass"; - if (bitCount == "bit8") { - generated = btoa(cryptoRandomString({length: 1, type: 'alphanumeric'})); - } - if (bitCount == "bit128") { - generated = btoa(cryptoRandomString({length: 16, type: 'alphanumeric'})); - } - if (bitCount == "bit256") { - generated = btoa(cryptoRandomString({length: 32, type: 'alphanumeric'})); - } - return generated; - }; + ({ devicePSKBitCount, passwordValue, textValue, className, variant, size, ...props }, ref) => { + const [pass, setPass] = useState(passwordValue ?? ""); + const [bitCount, setBits] = useState(devicePSKBitCount?.toString() ?? ""); return ( <> @@ -87,9 +74,9 @@ const Generator = React.forwardRef( - 256 bit - 128 bit - 8 bit + 256 bit + 128 bit + 8 bit + + + ); }, From cd0fcbbf900012d67742bf92f9180a44a9357fc7 Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Tue, 6 Aug 2024 00:50:17 -0400 Subject: [PATCH 05/10] initial working version --- src/components/Form/FormPasswordGenerator.tsx | 10 ++-- src/components/PageComponents/Channel.tsx | 29 ++++++++--- src/components/UI/Generator.tsx | 52 ++++++++----------- 3 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/components/Form/FormPasswordGenerator.tsx b/src/components/Form/FormPasswordGenerator.tsx index 762be7b0..7dc7281f 100644 --- a/src/components/Form/FormPasswordGenerator.tsx +++ b/src/components/Form/FormPasswordGenerator.tsx @@ -3,11 +3,11 @@ import type { GenericFormElementProps, } from "@components/Form/DynamicForm.js"; import { Generator } from "@components/UI/Generator.js"; -import { useState } from "react"; import { Controller, type FieldValues } from "react-hook-form"; export interface PasswordGeneratorProps extends BaseFormBuilderProps { type: "passwordGenerator"; + devicePSKBitCount: number; } export function PasswordGenerator({ @@ -20,11 +20,13 @@ export function PasswordGenerator({ control={control} render={({ field: { value, onChange, ...rest } }) => ( + {...rest} /> )} /> ); diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index b9c76612..942b6666 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -4,6 +4,8 @@ import { useToast } from "@core/hooks/useToast.js"; import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/js"; import { fromByteArray, toByteArray } from "base64-js"; +import cryptoRandomString from "crypto-random-string"; +import { useState } from "react"; export interface SettingsPanelProps { channel: Protobuf.Channel.Channel; @@ -13,12 +15,15 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { const { config, connection, addChannel } = useDevice(); const { toast } = useToast(); + const [pass, setPass] = useState(fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))); + const [bitCount, setBits] = useState(channel?.settings?.psk.length ?? 16); + const onSubmit = (data: ChannelValidation) => { const channel = new Protobuf.Channel.Channel({ ...data, settings: { ...data.settings, - psk: toByteArray(data.settings.psk ?? ""), + psk: toByteArray(pass), moduleSettings: { positionPrecision: data.settings.positionEnabled ? data.settings.preciseLocation @@ -36,6 +41,18 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }); }; + + const clickEventCb = (e) => { + setPass( + btoa( + cryptoRandomString({ + length: bitCount ?? 0, + type: "alphanumeric", + }), + ), + ); + } + return ( onSubmit={onSubmit} @@ -46,7 +63,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { ...{ settings: { ...channel?.settings, - psk: fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)), + psk: pass, positionEnabled: channel?.settings?.moduleSettings?.positionPrecision !== undefined && @@ -80,11 +97,11 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { name: "settings.psk", label: "pre-Shared Key", description: "256, 128, or 8 bit PSKs allowed", + devicePSKBitCount: bitCount ?? 0, properties: { - passwordValue: fromByteArray( - channel?.settings?.psk ?? new Uint8Array(0), - ), - devicePSKBitCount: channel?.settings?.psk.length, + value: pass, + onClick: clickEventCb, + changeEvent: (e: string) => setBits(parseInt(e)), }, }, { diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index bcc78c59..dc057336 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -10,9 +10,9 @@ import { SelectTrigger, SelectValue, } from "@components/UI/Select.js"; -import { cn } from "@core/utils/cn.js"; import cryptoRandomString from "crypto-random-string"; import { useState } from "react"; +import { fromByteArray, toByteArray } from "base64-js"; const generatorVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2", @@ -49,36 +49,41 @@ export interface GeneratorProps extends React.BaseHTMLAttributes, VariantProps { devicePSKBitCount?: number; - passwordValue?: string; - textValue?: string; + value: string; + buttonText?: string; + changeEvent: Function; + } -const Generator = React.forwardRef( +const getBitString = (bitcount?: number) => { + if (bitcount == 32) { + return "32" + } + if (bitcount == 1) { + return "1" + } + return "16" +} + +const Generator = React.forwardRef( ( { devicePSKBitCount, - passwordValue, - textValue, - className, + value, + buttonText, variant, - size, + changeEvent, ...props }, ref, ) => { - const [pass, setPass] = useState(passwordValue ?? ""); - const [bitCount, setBits] = useState( - devicePSKBitCount?.toString() ?? "", - ); return ( <> - + @@ -100,11 +91,7 @@ const Generator = React.forwardRef( - From faf094084cdddf99e435746b8ebb59fce41d5fee Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Tue, 6 Aug 2024 01:04:44 -0400 Subject: [PATCH 07/10] remove used imports and rename clickEventCb --- src/components/PageComponents/Channel.tsx | 4 ++-- src/components/UI/Generator.tsx | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index ddb4813c..3c79a543 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -45,7 +45,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }); }; - const clickEventCb = (e) => { + const clickEvent = () => { setPass( btoa( cryptoRandomString({ @@ -103,7 +103,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { devicePSKBitCount: bitCount ?? 0, properties: { value: pass, - onClick: clickEventCb, + onClick: clickEvent, changeEvent: (e: string) => setBits(Number.parseInt(e)), }, }, diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index 1e12d498..5b3dd44f 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -10,9 +10,6 @@ import { SelectTrigger, SelectValue, } from "@components/UI/Select.js"; -import { fromByteArray, toByteArray } from "base64-js"; -import cryptoRandomString from "crypto-random-string"; -import { useState } from "react"; const generatorVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2", From 1eedb6d97b33d8cb07c311c42d1d08b10aa2fdef Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sat, 10 Aug 2024 18:50:47 -0400 Subject: [PATCH 08/10] validation --- src/components/Form/DynamicForm.tsx | 5 ++ src/components/Form/FormPasswordGenerator.tsx | 12 +++- src/components/Form/FormWrapper.tsx | 5 ++ src/components/PageComponents/Channel.tsx | 54 ++++++++++++++- src/components/UI/Generator.tsx | 67 +++++-------------- src/components/UI/Input.tsx | 31 ++++++--- 6 files changed, 110 insertions(+), 64 deletions(-) diff --git a/src/components/Form/DynamicForm.tsx b/src/components/Form/DynamicForm.tsx index 4c9b551b..d70a0ee5 100644 --- a/src/components/Form/DynamicForm.tsx +++ b/src/components/Form/DynamicForm.tsx @@ -26,6 +26,7 @@ export interface BaseFormBuilderProps { disabledBy?: DisabledBy[]; label: string; description?: string; + validationText?: string; properties?: Record; } @@ -44,6 +45,8 @@ export interface DynamicFormProps { fieldGroups: { label: string; description: string; + valid?: boolean; + validationText?: string; fields: FieldProps[]; }[]; } @@ -98,6 +101,8 @@ export function DynamicForm({ key={field.label} label={field.label} description={field.description} + valid={field.validationText == undefined || field.validationText == ""} + validationText={field.validationText} > extends BaseFormBuilderProps { type: "passwordGenerator"; devicePSKBitCount: number; + inputChange: ChangeEventHandler; + selectChange: (event: string) => void; + buttonClick: MouseEventHandler; } export function PasswordGenerator({ @@ -18,12 +22,14 @@ export function PasswordGenerator({ ( + render={({ field: { value,...rest } }) => ( (
@@ -19,6 +23,7 @@ export const FieldWrapper = ({

{description}

+
{children}
diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index 3c79a543..85a6866c 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -21,6 +21,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { const [bitCount, setBits] = useState( channel?.settings?.psk.length ?? 16, ); + const [validationText, setValidationText] = useState(); const onSubmit = (data: ChannelValidation) => { const channel = new Protobuf.Channel.Channel({ @@ -54,8 +55,53 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }), ), ); + setValidationText(undefined); }; + const validatePass = (input: string, count: number) => { + if (count == 32) { + if (input.length != 44) { + setValidationText("Please enter a valid 256 bit PSK."); + } + else { + setValidationText(undefined); + } + } + else if (count == 16) + { + if (input.length != 24) { + setValidationText("Please enter a valid 128 bit PSK."); + } + else { + setValidationText(undefined); + } + } + else if (count == 1) + { + if (input.length != 4) { + setValidationText("Please enter a valid 1 bit PSK"); + } + else { + setValidationText(undefined); + } + } + else { + setValidationText("Unkown PSK length."); + } + } + + const inputChangeEvent = (e) => { + let psk = e.currentTarget?.value; + setPass(psk); + validatePass(psk, bitCount); + }; + + const selectChangeEvent = (e: string) => { + let count = Number.parseInt(e); + setBits(count); + validatePass(pass, count); + } + return ( onSubmit={onSubmit} @@ -100,11 +146,13 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { name: "settings.psk", label: "pre-Shared Key", description: "256, 128, or 8 bit PSKs allowed", + validationText: validationText, devicePSKBitCount: bitCount ?? 0, + inputChange: inputChangeEvent, + selectChange: selectChangeEvent, + buttonClick: clickEvent, properties: { - value: pass, - onClick: clickEvent, - changeEvent: (e: string) => setBits(Number.parseInt(e)), + value: pass }, }, { diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index 5b3dd44f..bdf0c3b8 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -1,4 +1,3 @@ -import { type VariantProps, cva } from "class-variance-authority"; import * as React from "react"; import { Button } from "@components/UI/Button.js"; @@ -11,67 +10,35 @@ import { SelectValue, } from "@components/UI/Select.js"; -const generatorVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2", - { - variants: { - variant: { - default: "", - destructive: - "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600", - success: - "bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600", - outline: - "bg-transparent border border-slate-200 hover:bg-slate-100 dark:border-slate-700 dark:text-slate-100", - subtle: - "bg-slate-100 text-slate-900 hover:bg-slate-200 dark:bg-slate-700 dark:text-slate-100", - ghost: - "bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent", - link: "bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent", - }, - size: { - default: "h-10 py-2 px-4", - sm: "h-9 px-2 rounded-md", - lg: "h-11 px-8 rounded-md", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -); - export interface GeneratorProps - extends React.BaseHTMLAttributes, - VariantProps { + extends React.BaseHTMLAttributes { devicePSKBitCount?: number; value: string; + variant: "default" | "invalid"; buttonText?: string; - changeEvent: (event: string) => void; + selectChange: (event: string) => void; + inputChange: (event: React.ChangeEvent) => void; + buttonClick: React.MouseEventHandler; } -const getBitString = (bitcount?: number) => { - if (bitcount === 32) { - return "32"; - } - if (bitcount === 1) { - return "1"; - } - return "16"; -}; const Generator = React.forwardRef( ( - { devicePSKBitCount, value, buttonText, variant, changeEvent, ...props }, + { devicePSKBitCount, variant, value, buttonText, selectChange, inputChange, buttonClick, ...props }, ref, ) => { return ( <> - + - @@ -97,4 +64,4 @@ const Generator = React.forwardRef( ); Generator.displayName = "Button"; -export { Generator, generatorVariants }; +export { Generator }; diff --git a/src/components/UI/Input.tsx b/src/components/UI/Input.tsx index e13f25f4..c7ac3217 100644 --- a/src/components/UI/Input.tsx +++ b/src/components/UI/Input.tsx @@ -2,9 +2,28 @@ import * as React from "react"; import { cn } from "@core/utils/cn.js"; import type { LucideIcon } from "lucide-react"; +import { cva, VariantProps } from "class-variance-authority"; + +const inputVariants = cva( + "flex h-10 w-full rounded-md border bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900", + { + variants: { + variant: { + default: + "border-slate-300 dark:border-slate-700", + invalid: + "border-red-500 dark:border-red-500", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); export interface InputProps - extends React.InputHTMLAttributes { + extends React.InputHTMLAttributes, + VariantProps { prefix?: string; suffix?: string; action?: { @@ -14,7 +33,7 @@ export interface InputProps } const Input = React.forwardRef( - ({ className, prefix, suffix, action, ...props }, ref) => { + ({ className, variant, prefix, suffix, action, ...props }, ref) => { return (
{prefix && ( @@ -23,11 +42,7 @@ const Input = React.forwardRef( )} @@ -51,4 +66,4 @@ const Input = React.forwardRef( ); Input.displayName = "Input"; -export { Input }; +export { Input, inputVariants }; From e7892fd6a04dbe8e17cab291d563602f836a852d Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sat, 10 Aug 2024 19:07:02 -0400 Subject: [PATCH 09/10] biome --- src/components/Form/DynamicForm.tsx | 5 ++- src/components/Form/FormPasswordGenerator.tsx | 4 +- src/components/Form/FormWrapper.tsx | 4 +- src/components/PageComponents/Channel.tsx | 38 ++++++++----------- src/components/UI/Generator.tsx | 28 ++++++++++---- src/components/UI/Input.tsx | 14 ++++--- 6 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/components/Form/DynamicForm.tsx b/src/components/Form/DynamicForm.tsx index d70a0ee5..470f36e7 100644 --- a/src/components/Form/DynamicForm.tsx +++ b/src/components/Form/DynamicForm.tsx @@ -101,7 +101,10 @@ export function DynamicForm({ key={field.label} label={field.label} description={field.description} - valid={field.validationText == undefined || field.validationText == ""} + valid={ + field.validationText === undefined || + field.validationText === "" + } validationText={field.validationText} > extends BaseFormBuilderProps { @@ -22,7 +22,7 @@ export function PasswordGenerator({ ( + render={({ field: { value, ...rest } }) => (

{description}

- +
{children}
diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index 85a6866c..fd425995 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -59,48 +59,40 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }; const validatePass = (input: string, count: number) => { - if (count == 32) { - if (input.length != 44) { + if (count === 32) { + if (input.length !== 44) { setValidationText("Please enter a valid 256 bit PSK."); - } - else { + } else { setValidationText(undefined); } - } - else if (count == 16) - { - if (input.length != 24) { + } else if (count === 16) { + if (input.length !== 24) { setValidationText("Please enter a valid 128 bit PSK."); - } - else { + } else { setValidationText(undefined); } - } - else if (count == 1) - { - if (input.length != 4) { + } else if (count === 1) { + if (input.length !== 4) { setValidationText("Please enter a valid 1 bit PSK"); - } - else { + } else { setValidationText(undefined); } - } - else { + } else { setValidationText("Unkown PSK length."); } - } + }; const inputChangeEvent = (e) => { - let psk = e.currentTarget?.value; + const psk = e.currentTarget?.value; setPass(psk); validatePass(psk, bitCount); }; const selectChangeEvent = (e: string) => { - let count = Number.parseInt(e); + const count = Number.parseInt(e); setBits(count); validatePass(pass, count); - } + }; return ( @@ -152,7 +144,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { selectChange: selectChangeEvent, buttonClick: clickEvent, properties: { - value: pass + value: pass, }, }, { diff --git a/src/components/UI/Generator.tsx b/src/components/UI/Generator.tsx index bdf0c3b8..344e89bb 100644 --- a/src/components/UI/Generator.tsx +++ b/src/components/UI/Generator.tsx @@ -10,8 +10,7 @@ import { SelectValue, } from "@components/UI/Select.js"; -export interface GeneratorProps - extends React.BaseHTMLAttributes { +export interface GeneratorProps extends React.BaseHTMLAttributes { devicePSKBitCount?: number; value: string; variant: "default" | "invalid"; @@ -21,17 +20,25 @@ export interface GeneratorProps buttonClick: React.MouseEventHandler; } - const Generator = React.forwardRef( ( - { devicePSKBitCount, variant, value, buttonText, selectChange, inputChange, buttonClick, ...props }, + { + devicePSKBitCount, + variant, + value, + buttonText, + selectChange, + inputChange, + buttonClick, + ...props + }, ref, ) => { return ( <> - ( - diff --git a/src/components/UI/Input.tsx b/src/components/UI/Input.tsx index c7ac3217..2c3080fb 100644 --- a/src/components/UI/Input.tsx +++ b/src/components/UI/Input.tsx @@ -1,18 +1,16 @@ import * as React from "react"; import { cn } from "@core/utils/cn.js"; +import { type VariantProps, cva } from "class-variance-authority"; import type { LucideIcon } from "lucide-react"; -import { cva, VariantProps } from "class-variance-authority"; const inputVariants = cva( "flex h-10 w-full rounded-md border bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900", { variants: { variant: { - default: - "border-slate-300 dark:border-slate-700", - invalid: - "border-red-500 dark:border-red-500", + default: "border-slate-300 dark:border-slate-700", + invalid: "border-red-500 dark:border-red-500", }, }, defaultVariants: { @@ -42,7 +40,11 @@ const Input = React.forwardRef( )} From 54a7b88146c26ee4cfabf4c2740ce3de35ee1c56 Mon Sep 17 00:00:00 2001 From: Hunter Thornsberry Date: Sat, 10 Aug 2024 19:50:21 -0400 Subject: [PATCH 10/10] rewrite PSK validation --- src/components/Form/DynamicForm.tsx | 1 - src/components/PageComponents/Channel.tsx | 24 ++++------------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/src/components/Form/DynamicForm.tsx b/src/components/Form/DynamicForm.tsx index 470f36e7..61c4c0e7 100644 --- a/src/components/Form/DynamicForm.tsx +++ b/src/components/Form/DynamicForm.tsx @@ -40,7 +40,6 @@ export interface DynamicFormProps { onSubmit: SubmitHandler; submitType?: "onChange" | "onSubmit"; hasSubmitButton?: boolean; - // defaultValues?: DeepPartial; defaultValues?: DefaultValues; fieldGroups: { label: string; diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index fd425995..d90e950b 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -59,30 +59,14 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }; const validatePass = (input: string, count: number) => { - if (count === 32) { - if (input.length !== 44) { - setValidationText("Please enter a valid 256 bit PSK."); - } else { - setValidationText(undefined); - } - } else if (count === 16) { - if (input.length !== 24) { - setValidationText("Please enter a valid 128 bit PSK."); - } else { - setValidationText(undefined); - } - } else if (count === 1) { - if (input.length !== 4) { - setValidationText("Please enter a valid 1 bit PSK"); - } else { - setValidationText(undefined); - } + if (input.length % 4 !== 0 || toByteArray(input).length !== count) { + setValidationText(`Please enter a valid ${count * 8} bit PSK.`); } else { - setValidationText("Unkown PSK length."); + setValidationText(undefined); } }; - const inputChangeEvent = (e) => { + const inputChangeEvent = (e: React.ChangeEvent) => { const psk = e.currentTarget?.value; setPass(psk); validatePass(psk, bitCount);