feat(help): enforce owner-aware high-risk contexts
Module Package Release / publish-packages (push) Successful in 13s
Module Package Release / publish-packages (push) Successful in 13s
This commit is contained in:
@@ -3,8 +3,9 @@ import { Dice5, Eye, EyeOff } from "lucide-react";
|
||||
import PasswordGeneratorDialog from "./PasswordGeneratorDialog";
|
||||
import type { PasswordGeneratorOptions } from "./passwordGenerator";
|
||||
import { usePlatformLanguage } from "../i18n/LanguageContext";
|
||||
import type { PlatformInterfaceIdentityProps } from "../types";
|
||||
|
||||
export type PasswordFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> & {
|
||||
export type PasswordFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "onChange"> & PlatformInterfaceIdentityProps & {
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
saved?: boolean;
|
||||
@@ -32,6 +33,10 @@ export default function PasswordField({
|
||||
className = "",
|
||||
inputClassName = "",
|
||||
id,
|
||||
interfaceId,
|
||||
helpContextId,
|
||||
helpModuleId,
|
||||
helpTopicId,
|
||||
...inputProps
|
||||
}: PasswordFieldProps) {
|
||||
const generatedId = useId();
|
||||
@@ -50,10 +55,20 @@ export default function PasswordField({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`password-field ${canReveal || canGenerate ? "has-actions" : ""} ${canGenerate ? "has-generator" : ""} ${canReveal ? "has-reveal" : ""} ${showSavedPlaceholder ? "is-saved-empty" : ""} ${className}`.trim()}>
|
||||
<div
|
||||
className={`password-field ${canReveal || canGenerate ? "has-actions" : ""} ${canGenerate ? "has-generator" : ""} ${canReveal ? "has-reveal" : ""} ${showSavedPlaceholder ? "is-saved-empty" : ""} ${className}`.trim()}
|
||||
data-help-scope="field"
|
||||
data-interface-id={interfaceId}
|
||||
data-help-context-id={helpContextId}
|
||||
data-help-module-id={helpModuleId}
|
||||
data-help-topic-id={helpTopicId}
|
||||
>
|
||||
<input
|
||||
{...inputProps}
|
||||
id={inputId}
|
||||
data-help-context-id={helpContextId}
|
||||
data-help-module-id={helpModuleId}
|
||||
data-help-topic-id={helpTopicId}
|
||||
className={inputClassName}
|
||||
type={inputType}
|
||||
value={value}
|
||||
@@ -72,6 +87,9 @@ export default function PasswordField({
|
||||
className="password-field-action"
|
||||
aria-label={translatedGeneratorLabel}
|
||||
title={translatedGeneratorLabel}
|
||||
data-help-context-id={helpContextId}
|
||||
data-help-module-id={helpModuleId}
|
||||
data-help-topic-id={helpTopicId}
|
||||
onClick={() => setGeneratorOpen(true)}
|
||||
>
|
||||
<Dice5 size={17} aria-hidden="true" />
|
||||
@@ -83,6 +101,9 @@ export default function PasswordField({
|
||||
className="password-field-action"
|
||||
aria-label={visible ? translatedHideLabel : translatedRevealLabel}
|
||||
title={visible ? translatedHideLabel : translatedRevealLabel}
|
||||
data-help-context-id={helpContextId}
|
||||
data-help-module-id={helpModuleId}
|
||||
data-help-topic-id={helpTopicId}
|
||||
onClick={() => setVisible((current) => !current)}
|
||||
>
|
||||
{visible ? <EyeOff size={17} aria-hidden="true" /> : <Eye size={17} aria-hidden="true" />}
|
||||
@@ -95,6 +116,9 @@ export default function PasswordField({
|
||||
<PasswordGeneratorDialog
|
||||
open={generatorOpen}
|
||||
initialOptions={generatorOptions}
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
onUse={(password) => {
|
||||
onValueChange(password);
|
||||
setVisible(false);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
type PasswordGeneratorOptions
|
||||
} from "./passwordGenerator";
|
||||
import { usePlatformLanguage } from "../i18n/LanguageContext";
|
||||
import type { PlatformInterfaceIdentityProps } from "../types";
|
||||
|
||||
const GENERATION_ERROR_LABELS: Record<PasswordGeneratorErrorCode, string> = {
|
||||
"invalid-length": "i18n:govoplan-core.password_length_must_be_between_12_and_128_character.4d147c07",
|
||||
@@ -23,7 +24,7 @@ const GENERATION_ERROR_LABELS: Record<PasswordGeneratorErrorCode, string> = {
|
||||
"secure-random-unavailable": "i18n:govoplan-core.secure_browser_password_generation_is_unavailable.55275f10"
|
||||
};
|
||||
|
||||
export type PasswordGeneratorDialogProps = {
|
||||
export type PasswordGeneratorDialogProps = PlatformInterfaceIdentityProps & {
|
||||
open: boolean;
|
||||
initialOptions?: Partial<PasswordGeneratorOptions>;
|
||||
onUse: (password: string) => void;
|
||||
@@ -33,6 +34,9 @@ export type PasswordGeneratorDialogProps = {
|
||||
export default function PasswordGeneratorDialog({
|
||||
open,
|
||||
initialOptions,
|
||||
helpContextId,
|
||||
helpModuleId,
|
||||
helpTopicId,
|
||||
onUse,
|
||||
onClose
|
||||
}: PasswordGeneratorDialogProps) {
|
||||
@@ -95,6 +99,9 @@ export default function PasswordGeneratorDialog({
|
||||
bodyClassName="password-generator-body"
|
||||
footerClassName="button-row compact-actions"
|
||||
portal
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
onClose={onClose}
|
||||
footer={(
|
||||
<>
|
||||
@@ -103,6 +110,9 @@ export default function PasswordGeneratorDialog({
|
||||
type="button"
|
||||
variant="primary"
|
||||
disabled={!candidate}
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
onClick={() => {
|
||||
if (!candidate) return;
|
||||
onUse(candidate);
|
||||
@@ -116,7 +126,7 @@ export default function PasswordGeneratorDialog({
|
||||
>
|
||||
{error ? <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert> : null}
|
||||
<div className="password-generator-options">
|
||||
<FormField label="i18n:govoplan-core.length.adc95605">
|
||||
<FormField label="i18n:govoplan-core.length.adc95605" helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId}>
|
||||
<input
|
||||
type="number"
|
||||
min={12}
|
||||
@@ -127,13 +137,13 @@ export default function PasswordGeneratorDialog({
|
||||
/>
|
||||
</FormField>
|
||||
<div className="password-generator-character-sets" aria-label={translateText("i18n:govoplan-core.character_sets.db6efda2")}>
|
||||
<ToggleSwitch label="i18n:govoplan-core.lowercase.3b677a18" checked={options.lowercase} onChange={(checked) => setOption("lowercase", checked)} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.uppercase.b463d690" checked={options.uppercase} onChange={(checked) => setOption("uppercase", checked)} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.digits.9cd500d3" checked={options.digits} onChange={(checked) => setOption("digits", checked)} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.symbols.9491fc41" checked={options.symbols} onChange={(checked) => setOption("symbols", checked)} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.lowercase.3b677a18" checked={options.lowercase} onChange={(checked) => setOption("lowercase", checked)} helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.uppercase.b463d690" checked={options.uppercase} onChange={(checked) => setOption("uppercase", checked)} helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.digits.9cd500d3" checked={options.digits} onChange={(checked) => setOption("digits", checked)} helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId} />
|
||||
<ToggleSwitch label="i18n:govoplan-core.symbols.9491fc41" checked={options.symbols} onChange={(checked) => setOption("symbols", checked)} helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId} />
|
||||
</div>
|
||||
</div>
|
||||
<FormField label="i18n:govoplan-core.generated_password.78461854">
|
||||
<FormField label="i18n:govoplan-core.generated_password.78461854" helpContextId={helpContextId} helpModuleId={helpModuleId} helpTopicId={helpTopicId}>
|
||||
<div className="password-generator-result">
|
||||
<input
|
||||
type="text"
|
||||
@@ -147,11 +157,17 @@ export default function PasswordGeneratorDialog({
|
||||
icon={<Copy size={16} />}
|
||||
onClick={() => void copy()}
|
||||
disabled={!candidate || typeof navigator === "undefined" || !navigator.clipboard?.writeText}
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
/>
|
||||
<IconButton
|
||||
label="i18n:govoplan-core.generate_another_password.d99fc019"
|
||||
icon={<RefreshCw size={16} />}
|
||||
onClick={generate}
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
/>
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
@@ -57,13 +57,13 @@ export default function LoginModal({
|
||||
<FormLayout columns={1} collapseAt="standard" id={formId} className="" onSubmit={submit}>
|
||||
{message && <DismissibleAlert tone="info" dismissible={false}>{message}</DismissibleAlert>}
|
||||
{error && <DismissibleAlert tone="danger" resetKey={error}>{error}</DismissibleAlert>}
|
||||
<FormField label="i18n:govoplan-core.email.84add5b2">
|
||||
<input type="email" value={email} autoComplete="username" onChange={(e) => setEmail(e.target.value)} />
|
||||
<FormField label="i18n:govoplan-core.email.84add5b2" helpContextId="access.authentication.email" helpModuleId="access">
|
||||
<input data-help-context-id="access.authentication.email" data-help-module-id="access" type="email" value={email} autoComplete="username" onChange={(e) => setEmail(e.target.value)} />
|
||||
</FormField>
|
||||
<FormField label="i18n:govoplan-core.password.8be3c943">
|
||||
<PasswordField value={password} autoComplete="current-password" onValueChange={setPassword} />
|
||||
<FormField label="i18n:govoplan-core.password.8be3c943" helpContextId="access.authentication.password" helpModuleId="access">
|
||||
<PasswordField helpContextId="access.authentication.password" helpModuleId="access" value={password} autoComplete="current-password" onValueChange={setPassword} />
|
||||
</FormField>
|
||||
</FormLayout>
|
||||
</Dialog>);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,8 +572,10 @@ export default function SettingsPage({
|
||||
<FormField label="i18n:govoplan-core.api_base_url.1358fba4" help="i18n:govoplan-core.leave_empty_to_use_the_same_origin_in_vite_dev_a.9a1c25d7">
|
||||
<input value={settings.apiBaseUrl} onChange={(e) => onSettingsChange({ ...settings, apiBaseUrl: e.target.value })} placeholder="https://example.org or empty" />
|
||||
</FormField>
|
||||
<FormField label="i18n:govoplan-core.automation_api_key.5d4e2e6e" help="i18n:govoplan-core.used_only_when_there_is_no_browser_session_token.9d399e70">
|
||||
<FormField label="i18n:govoplan-core.automation_api_key.5d4e2e6e" help="i18n:govoplan-core.used_only_when_there_is_no_browser_session_token.9d399e70" helpContextId="access.settings.automation-api-key" helpModuleId="access">
|
||||
<PasswordField
|
||||
helpContextId="access.settings.automation-api-key"
|
||||
helpModuleId="access"
|
||||
value={settings.apiKey}
|
||||
autoComplete="off"
|
||||
onValueChange={(apiKey) => onSettingsChange({ ...settings, apiKey })} />
|
||||
|
||||
Reference in New Issue
Block a user