fix encryption validation

This commit is contained in:
Ildar Kamalov
2025-02-05 17:52:45 +03:00
parent 6fef2df6b9
commit 8b6d785c72
4 changed files with 74 additions and 55 deletions

8
.gitignore vendored
View File

@@ -19,6 +19,10 @@
/agh-backup/ /agh-backup/
/bin/ /bin/
/build/* /build/*
/client/blob-report/
/client/playwright-report/
/client/playwright/.cache/
/client/test-results/
/data/ /data/
/dist/ /dist/
/filtering/tests/filtering.TestLotsOfRules*.pprof /filtering/tests/filtering.TestLotsOfRules*.pprof
@@ -33,9 +37,5 @@ AdGuardHome.exe
AdGuardHome.yaml* AdGuardHome.yaml*
coverage.txt coverage.txt
node_modules/ node_modules/
/client/blob-report/
/client/playwright-report/
/client/playwright/.cache/
/client/test-results/
!/build/gitkeep !/build/gitkeep

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'; import React from 'react';
import { Trans, useTranslation } from 'react-i18next'; import { Trans, useTranslation } from 'react-i18next';
@@ -92,19 +92,7 @@ export type EncryptionFormValues = {
type Props = { type Props = {
initialValues: EncryptionFormValues; initialValues: EncryptionFormValues;
processingConfig: boolean; encryption: EncryptionData;
processingValidate: boolean;
status_key?: string;
not_after?: string;
warning_validation?: string;
valid_chain?: boolean;
valid_key?: boolean;
valid_cert?: boolean;
valid_pair?: boolean;
dns_names?: string[];
key_type?: string;
issuer?: string;
subject?: string;
onSubmit: (values: EncryptionFormValues) => void; onSubmit: (values: EncryptionFormValues) => void;
debouncedConfigValidation: (values: EncryptionFormValues) => void; debouncedConfigValidation: (values: EncryptionFormValues) => void;
setTlsConfig: (values: Partial<EncryptionData>) => void; setTlsConfig: (values: Partial<EncryptionData>) => void;
@@ -130,24 +118,28 @@ const defaultValues = {
export const Form = ({ export const Form = ({
initialValues, initialValues,
processingConfig, encryption,
processingValidate,
not_after,
valid_chain,
valid_key,
valid_cert,
valid_pair,
dns_names,
key_type,
issuer,
subject,
warning_validation,
onSubmit, onSubmit,
setTlsConfig, setTlsConfig,
debouncedConfigValidation,
validateTlsConfig, validateTlsConfig,
}: Props) => { }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const previousValuesRef = useRef<EncryptionFormValues>(initialValues);
const {
not_after,
valid_chain,
valid_key,
valid_cert,
valid_pair,
dns_names,
key_type,
issuer,
subject,
warning_validation,
processingConfig,
processingValidate,
} = encryption;
const { const {
control, control,
@@ -166,8 +158,6 @@ export const Form = ({
mode: 'onBlur', mode: 'onBlur',
}); });
const watchedValues = watch();
const { const {
enabled: isEnabled, enabled: isEnabled,
serve_plain_dns: servePlainDns, serve_plain_dns: servePlainDns,
@@ -178,16 +168,11 @@ export const Form = ({
private_key_saved: privateKeySaved, private_key_saved: privateKeySaved,
certificate_path: certificatePath, certificate_path: certificatePath,
certificate_source: certificateSource, certificate_source: certificateSource,
} = watchedValues; } = watch();
useEffect(() => { const handleBlur = () => {
const previousValues = previousValuesRef.current; debouncedConfigValidation(getValues());
};
if (JSON.stringify(previousValues) !== JSON.stringify(watchedValues)) {
// TODO(ik) onChange TLS config validation
previousValuesRef.current = watchedValues;
}
}, [watchedValues]);
const isSavingDisabled = () => { const isSavingDisabled = () => {
const processing = isSubmitting || processingConfig || processingValidate; const processing = isSubmitting || processingConfig || processingValidate;
@@ -243,7 +228,9 @@ export const Form = ({
<Controller <Controller
name="enabled" name="enabled"
control={control} control={control}
render={({ field }) => <Checkbox {...field} title={t('encryption_enable')} />} render={({ field }) => (
<Checkbox {...field} title={t('encryption_enable')} onBlur={handleBlur} />
)}
/> />
</div> </div>
@@ -288,6 +275,7 @@ export const Form = ({
placeholder={t('encryption_server_enter')} placeholder={t('encryption_server_enter')}
error={fieldState.error?.message} error={fieldState.error?.message}
disabled={!isEnabled} disabled={!isEnabled}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -337,6 +325,7 @@ export const Form = ({
const { value } = e.target; const { value } = e.target;
field.onChange(toNumber(value)); field.onChange(toNumber(value));
}} }}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -368,6 +357,7 @@ export const Form = ({
const { value } = e.target; const { value } = e.target;
field.onChange(toNumber(value)); field.onChange(toNumber(value));
}} }}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -399,6 +389,7 @@ export const Form = ({
const { value } = e.target; const { value } = e.target;
field.onChange(toNumber(value)); field.onChange(toNumber(value));
}} }}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -457,6 +448,7 @@ export const Form = ({
placeholder={t('encryption_certificates_input')} placeholder={t('encryption_certificates_input')}
disabled={!isEnabled} disabled={!isEnabled}
error={fieldState.error?.message} error={fieldState.error?.message}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -471,6 +463,7 @@ export const Form = ({
placeholder={t('encryption_certificate_path')} placeholder={t('encryption_certificate_path')}
error={fieldState.error?.message} error={fieldState.error?.message}
disabled={!isEnabled} disabled={!isEnabled}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -527,6 +520,7 @@ export const Form = ({
} }
field.onChange(checked); field.onChange(checked);
}} }}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -540,6 +534,7 @@ export const Form = ({
placeholder={t('encryption_key_input')} placeholder={t('encryption_key_input')}
disabled={!isEnabled || privateKeySaved} disabled={!isEnabled || privateKeySaved}
error={fieldState.error?.message} error={fieldState.error?.message}
onBlur={handleBlur}
/> />
)} )}
/> />
@@ -555,6 +550,7 @@ export const Form = ({
placeholder={t('encryption_private_key_path')} placeholder={t('encryption_private_key_path')}
error={fieldState.error?.message} error={fieldState.error?.message}
disabled={!isEnabled} disabled={!isEnabled}
onBlur={handleBlur}
/> />
)} )}
/> />

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants'; import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
@@ -18,19 +18,37 @@ type Props = {
export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => { export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => {
if (encryption.enabled) {
validateTlsConfig(encryption);
}
}, [encryption, validateTlsConfig]);
const initialValues = useMemo((): EncryptionFormValues => { const initialValues = useMemo((): EncryptionFormValues => {
const { certificate_chain, private_key, private_key_saved } = encryption; const {
enabled,
serve_plain_dns,
server_name,
force_https,
port_https,
port_dns_over_tls,
port_dns_over_quic,
certificate_chain,
private_key,
certificate_path,
private_key_path,
private_key_saved,
} = encryption;
const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH; const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH; const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
return { return {
...encryption, enabled,
serve_plain_dns,
server_name,
force_https,
port_https,
port_dns_over_tls,
port_dns_over_quic,
certificate_chain,
private_key,
certificate_path,
private_key_path,
private_key_saved,
certificate_source, certificate_source,
key_source, key_source,
}; };
@@ -75,7 +93,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
} }
}, []); }, []);
const debouncedConfigValidation = useCallback(debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]); const debouncedConfigValidation = useMemo(() => debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]);
return ( return (
<div className="encryption"> <div className="encryption">
@@ -94,7 +112,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
debouncedConfigValidation={debouncedConfigValidation} debouncedConfigValidation={debouncedConfigValidation}
setTlsConfig={setTlsConfig} setTlsConfig={setTlsConfig}
validateTlsConfig={validateTlsConfig} validateTlsConfig={validateTlsConfig}
{...encryption} encryption={encryption}
/> />
</Card> </Card>
)} )}

View File

@@ -12,10 +12,14 @@ type Props = {
className?: string; className?: string;
error?: string; error?: string;
onChange: (value: boolean) => void; onChange: (value: boolean) => void;
onBlur?: () => void;
}; };
export const Checkbox = forwardRef<HTMLInputElement, Props>( export const Checkbox = forwardRef<HTMLInputElement, Props>(
({ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, ...rest }, ref) => ( (
{ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, onBlur, ...rest },
ref,
) => (
<> <>
<label className={clsx('checkbox', className)}> <label className={clsx('checkbox', className)}>
<span className="checkbox__marker" /> <span className="checkbox__marker" />
@@ -26,6 +30,7 @@ export const Checkbox = forwardRef<HTMLInputElement, Props>(
disabled={disabled} disabled={disabled}
checked={value} checked={value}
onChange={(e) => onChange(e.target.checked)} onChange={(e) => onChange(e.target.checked)}
onBlur={onBlur}
ref={ref} ref={ref}
{...rest} {...rest}
/> />