Fix: Editing a secret clears its masked value
This commit is contained in:
@@ -48,6 +48,7 @@ import { jmapGet, jmapSet, jmapRequest, getAccountId } from '@/services/jmap/cli
|
||||
import { calculateJmapPatch } from '@/lib/jmapPatch';
|
||||
import { friendlySetError } from '@/lib/jmapErrors';
|
||||
import { coerceLabel } from '@/lib/objectOptions';
|
||||
import { SECRET_MASK } from '@/lib/jmapUtils';
|
||||
import { toast } from '@/hooks/use-toast';
|
||||
import { logFormChange } from '@/lib/debug';
|
||||
import { FieldWidget } from '@/components/forms/FieldWidget';
|
||||
@@ -468,7 +469,7 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
|
||||
const isSecret =
|
||||
fieldDef.type.type === 'string' &&
|
||||
(fieldDef.type.format === 'secret' || fieldDef.type.format === 'secretText');
|
||||
if (isSecret && formData[fieldName] === '*****') continue;
|
||||
if (isSecret && formData[fieldName] === SECRET_MASK) continue;
|
||||
createPayload[fieldName] = formData[fieldName];
|
||||
}
|
||||
}
|
||||
@@ -531,7 +532,7 @@ export function DynamicForm({ viewName, objectId }: DynamicFormProps) {
|
||||
if (
|
||||
fieldDef?.type.type === 'string' &&
|
||||
(fieldDef.type.format === 'secret' || fieldDef.type.format === 'secretText') &&
|
||||
patchValue === '*****'
|
||||
patchValue === SECRET_MASK
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -1101,8 +1102,6 @@ function buildRenderableField(
|
||||
return { formField, field, visible, enterpriseDisabled };
|
||||
}
|
||||
|
||||
const SECRET_MASK_PLACEHOLDER = '*****';
|
||||
|
||||
function isOtpAuthValid(data: unknown): boolean {
|
||||
if (data == null || typeof data !== 'object' || Array.isArray(data)) return true;
|
||||
const obj = data as Record<string, unknown>;
|
||||
@@ -1118,7 +1117,7 @@ function isOtpAuthValid(data: unknown): boolean {
|
||||
const otpUrl = obj.otpUrl;
|
||||
const otpCode = obj.otpCode;
|
||||
if (otpUrl == null || otpUrl === '') return true;
|
||||
if (otpCode == null || otpCode === '' || otpCode === SECRET_MASK_PLACEHOLDER) {
|
||||
if (otpCode == null || otpCode === '' || otpCode === SECRET_MASK) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -41,6 +41,7 @@ import { resolveSchema, resolveVariantForm, resolveObject, buildEmbeddedDefaults
|
||||
import { useAccountStore } from '@/stores/accountStore';
|
||||
import { useEffectiveEdition } from '@/components/forms/FormEditionContext';
|
||||
import { useObjectList, useObjectLabel, useNoPermissionMessage, type ObjectOption } from '@/lib/objectOptions';
|
||||
import { SECRET_MASK } from '@/lib/jmapUtils';
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command';
|
||||
import { jmapGet, getAccountId } from '@/services/jmap/client';
|
||||
|
||||
@@ -457,8 +458,6 @@ interface SecretInputProps {
|
||||
multiline: boolean;
|
||||
}
|
||||
|
||||
const SECRET_MASK = '****';
|
||||
|
||||
function SecretInput({ value, onChange, readOnly, placeholder, minLength, maxLength, multiline }: SecretInputProps) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [localValue, setLocalValue] = useState(() => (value === SECRET_MASK || value === '' ? '' : value));
|
||||
@@ -482,6 +481,7 @@ function SecretInput({ value, onChange, readOnly, placeholder, minLength, maxLen
|
||||
};
|
||||
|
||||
const commit = () => {
|
||||
if (isMasked && localValue === '') return;
|
||||
if (localValue !== value) onChange(localValue);
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Loader2, ShieldCheck, ShieldOff } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { SECRET_MASK } from '@/lib/jmapUtils';
|
||||
|
||||
interface OtpAuthValue {
|
||||
otpUrl?: string | null;
|
||||
@@ -25,8 +26,6 @@ interface OtpAuthFieldProps {
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
const SECRET_MASK = '*****';
|
||||
|
||||
const STALWART_IMAGE_URL = 'https://stalw.art/img/favicon-32x32.png';
|
||||
|
||||
function buildOtpAuthUrl(totp: OTPAuth.TOTP): string {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Check, X, HelpCircle, ChevronRight } from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { jmapMapToArray } from '@/lib/jmapUtils';
|
||||
import { jmapMapToArray, SECRET_MASK } from '@/lib/jmapUtils';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||
@@ -212,7 +212,7 @@ function StringValue({ value, format }: { value: unknown; format: string }) {
|
||||
return <pre className="whitespace-pre-wrap break-all rounded bg-muted/50 p-2 text-xs font-mono">{str}</pre>;
|
||||
}
|
||||
if (format === 'secret' || format === 'secretText') {
|
||||
return <span className="text-muted-foreground">*****</span>;
|
||||
return <span className="text-muted-foreground">{SECRET_MASK}</span>;
|
||||
}
|
||||
return <span className="break-all">{str}</span>;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSchemaStore } from '@/stores/schemaStore';
|
||||
import { useAccountStore } from '@/stores/accountStore';
|
||||
import { resolveObject, resolveSchema, resolveVariantForm } from '@/lib/schemaResolver';
|
||||
import { SECRET_MASK } from '@/lib/jmapUtils';
|
||||
import { jmapSet, getAccountId } from '@/services/jmap/client';
|
||||
import { FieldWidget } from '@/components/forms/FieldWidget';
|
||||
import { DynamicView } from '@/components/views/DynamicView';
|
||||
@@ -104,7 +105,7 @@ export function ActionPage({ viewName }: ActionPageProps) {
|
||||
if (name in formData) {
|
||||
const isSecret =
|
||||
def.type.type === 'string' && (def.type.format === 'secret' || def.type.format === 'secretText');
|
||||
if (isSecret && formData[name] === '*****') continue;
|
||||
if (isSecret && formData[name] === SECRET_MASK) continue;
|
||||
payload[name] = formData[name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
export const SECRET_MASK = '****';
|
||||
|
||||
export function jmapMapToArray<T>(val: unknown): T[] {
|
||||
if (Array.isArray(val)) return val as T[];
|
||||
if (val && typeof val === 'object') {
|
||||
|
||||
Reference in New Issue
Block a user