Use managed secret controls for Files connectors
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
||||
LoadingFrame,
|
||||
mergeDeltaRows,
|
||||
SegmentedControl,
|
||||
StatusBadge,
|
||||
TableActionGroup,
|
||||
ToggleSwitch,
|
||||
useDeltaWatermarks,
|
||||
useUnsavedDraftGuard,
|
||||
@@ -65,8 +67,6 @@ type ConnectorProfileDraft = {
|
||||
username: string;
|
||||
password: string;
|
||||
token: string;
|
||||
passwordEnv: string;
|
||||
tokenEnv: string;
|
||||
secretRef: string;
|
||||
canBrowse: boolean;
|
||||
canImport: boolean;
|
||||
@@ -91,8 +91,6 @@ type ConnectorCredentialDraft = {
|
||||
username: string;
|
||||
password: string;
|
||||
token: string;
|
||||
passwordEnv: string;
|
||||
tokenEnv: string;
|
||||
secretRef: string;
|
||||
policyMode: PolicyMode;
|
||||
allowedPaths: string;
|
||||
@@ -491,8 +489,6 @@ export default function FileConnectorSettingsPanel({
|
||||
username: cleanOrNull(credentialDraft.username),
|
||||
password: cleanOrUndefined(credentialDraft.password),
|
||||
token: cleanOrUndefined(credentialDraft.token),
|
||||
password_env: cleanOrNull(credentialDraft.passwordEnv),
|
||||
token_env: cleanOrNull(credentialDraft.tokenEnv),
|
||||
secret_ref: cleanOrNull(credentialDraft.secretRef)
|
||||
};
|
||||
const sharedPayload = {
|
||||
@@ -633,8 +629,6 @@ export default function FileConnectorSettingsPanel({
|
||||
username: cleanOrNull(draft.username),
|
||||
password: cleanOrUndefined(draft.password),
|
||||
token: cleanOrUndefined(draft.token),
|
||||
password_env: cleanOrNull(draft.passwordEnv),
|
||||
token_env: cleanOrNull(draft.tokenEnv),
|
||||
secret_ref: cleanOrNull(draft.secretRef)
|
||||
},
|
||||
metadata: metadataFromDraft(draft)
|
||||
@@ -680,8 +674,6 @@ export default function FileConnectorSettingsPanel({
|
||||
username: cleanOrNull(credentialDraft.username),
|
||||
password: cleanOrUndefined(credentialDraft.password),
|
||||
token: cleanOrUndefined(credentialDraft.token),
|
||||
password_env: cleanOrNull(credentialDraft.passwordEnv),
|
||||
token_env: cleanOrNull(credentialDraft.tokenEnv),
|
||||
secret_ref: cleanOrNull(credentialDraft.secretRef)
|
||||
},
|
||||
metadata: profile.metadata ?? {},
|
||||
@@ -756,7 +748,7 @@ export default function FileConnectorSettingsPanel({
|
||||
width: "120px",
|
||||
render: (row) => {
|
||||
const enabled = row.kind === "profile" ? row.profile.enabled : row.credential.enabled;
|
||||
return <span className={`status-badge ${enabled ? "success" : "neutral"}`}>{enabled ? "i18n:govoplan-files.enabled.df174a3f" : "i18n:govoplan-files.disabled.f4f4473d"}</span>;
|
||||
return <StatusBadge status={enabled ? "success" : "inactive"} label={enabled ? "i18n:govoplan-files.enabled.df174a3f" : "i18n:govoplan-files.disabled.f4f4473d"} />;
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -771,21 +763,19 @@ export default function FileConnectorSettingsPanel({
|
||||
function renderConnectorActions(row: ConnectorTreeRow) {
|
||||
if (row.kind === "credential") {
|
||||
const readOnly = row.credential.source_kind !== "database";
|
||||
return (
|
||||
<div className="admin-icon-actions">
|
||||
<Button type="button" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.credential.label })} aria-label={i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.credential.label })} onClick={() => startCredentialEdit(row.credential)} disabled={!canWrite || readOnly || saving}><Edit3 size={15} /></Button>
|
||||
<Button type="button" variant="danger" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.credential.label })} aria-label={i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.credential.label })} onClick={() => setPendingCredentialDeactivate(row.credential)} disabled={!canWrite || readOnly || saving || !row.credential.enabled}><Trash2 size={15} /></Button>
|
||||
</div>);
|
||||
return <TableActionGroup actions={[
|
||||
{ id: "edit", label: i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.credential.label }), icon: <Edit3 size={15} />, disabled: !canWrite || readOnly || saving, onClick: () => startCredentialEdit(row.credential) },
|
||||
{ id: "disable", label: i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.credential.label }), icon: <Trash2 size={15} />, variant: "danger", applicable: row.credential.enabled, disabled: !canWrite || readOnly || saving, onClick: () => setPendingCredentialDeactivate(row.credential) }
|
||||
]} />;
|
||||
|
||||
}
|
||||
const readOnly = row.profile.source_kind !== "database";
|
||||
return (
|
||||
<div className="admin-icon-actions">
|
||||
<Button type="button" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.test_value.6a5d10a5", { value0: row.profile.label })} aria-label={i18nMessage("i18n:govoplan-files.test_value.6a5d10a5", { value0: row.profile.label })} onClick={() => void testBrowse(row.profile)} disabled={saving || testingProfileId === row.profile.id || !row.profile.enabled}><RefreshCw size={15} /></Button>
|
||||
<Button type="button" variant="primary" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.add_credential_for_value.0fa9c1fe", { value0: row.profile.label })} aria-label={i18nMessage("i18n:govoplan-files.add_credential_for_value.0fa9c1fe", { value0: row.profile.label })} onClick={() => startCredentialCreate(row.profile)} disabled={!canWrite || saving}><UserRoundKey size={15} /></Button>
|
||||
<Button type="button" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.profile.label })} aria-label={i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.profile.label })} onClick={() => startEdit(row.profile)} disabled={!canWrite || readOnly || saving}><Edit3 size={15} /></Button>
|
||||
<Button type="button" variant="danger" className="admin-icon-button" title={i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.profile.label })} aria-label={i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.profile.label })} onClick={() => setPendingDeactivate(row.profile)} disabled={!canWrite || readOnly || saving || !row.profile.enabled}><Trash2 size={15} /></Button>
|
||||
</div>);
|
||||
return <TableActionGroup actions={[
|
||||
{ id: "test", label: i18nMessage("i18n:govoplan-files.test_value.6a5d10a5", { value0: row.profile.label }), icon: <RefreshCw size={15} />, applicable: row.profile.enabled, disabled: saving || testingProfileId === row.profile.id, onClick: () => void testBrowse(row.profile) },
|
||||
{ id: "add-credential", label: i18nMessage("i18n:govoplan-files.add_credential_for_value.0fa9c1fe", { value0: row.profile.label }), icon: <UserRoundKey size={15} />, variant: "primary", disabled: !canWrite || saving, onClick: () => startCredentialCreate(row.profile) },
|
||||
{ id: "edit", label: i18nMessage("i18n:govoplan-files.edit_value.fad75899", { value0: row.profile.label }), icon: <Edit3 size={15} />, disabled: !canWrite || readOnly || saving, onClick: () => startEdit(row.profile) },
|
||||
{ id: "disable", label: i18nMessage("i18n:govoplan-files.disable_value.09485f7f", { value0: row.profile.label }), icon: <Trash2 size={15} />, variant: "danger", applicable: row.profile.enabled, disabled: !canWrite || readOnly || saving, onClick: () => setPendingDeactivate(row.profile) }
|
||||
]} />;
|
||||
|
||||
}
|
||||
|
||||
@@ -865,7 +855,7 @@ export default function FileConnectorSettingsPanel({
|
||||
|
||||
<LoadingFrame loading={loading} label="i18n:govoplan-files.loading_connector_policy.f12ab285">
|
||||
<>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.allowed_connection_ids.0df854c8">
|
||||
<textarea value={policyDraft.allowedConnectors} disabled={saving || !canWrite} onChange={(event) => patchPolicyDraft({ allowedConnectors: event.target.value })} rows={3} placeholder={"tenant-webdav\nseafile-*"} />
|
||||
</FormField>
|
||||
@@ -940,7 +930,7 @@ export default function FileConnectorSettingsPanel({
|
||||
<h3>Credential</h3>
|
||||
<p>Choose where this credential can be used and how it signs in.</p>
|
||||
</header>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.credential_id.9432a6e1">
|
||||
<input className={!editingCredentialId && !credentialDraft.id.trim() ? "field-input-missing" : undefined} aria-invalid={!editingCredentialId && !credentialDraft.id.trim() || undefined} value={credentialDraft.id} disabled={Boolean(editingCredentialId) || saving} onChange={(event) => patchCredentialDraft({ id: event.target.value })} placeholder={`${scopeType}-webdav-credentials`} />
|
||||
</FormField>
|
||||
@@ -1018,7 +1008,7 @@ export default function FileConnectorSettingsPanel({
|
||||
onChange={patchCredentialValues}
|
||||
disabled={saving}
|
||||
showPassword={false} />
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.secret_reference.04ed2221">
|
||||
<input value={credentialDraft.secretRef} disabled={saving} onChange={(event) => patchCredentialDraft({ secretRef: event.target.value })} />
|
||||
</FormField>
|
||||
@@ -1049,16 +1039,6 @@ export default function FileConnectorSettingsPanel({
|
||||
{credentialLoginResult.message}
|
||||
</DismissibleAlert>
|
||||
}
|
||||
<AdvancedOptionsPanel title="Environment and fallback secrets" summary="Use these only when the deployment injects secrets outside the database.">
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<FormField label="i18n:govoplan-files.password_environment_variable.0e77673f">
|
||||
<input value={credentialDraft.passwordEnv} disabled={saving} onChange={(event) => patchCredentialDraft({ passwordEnv: event.target.value })} />
|
||||
</FormField>
|
||||
<FormField label="i18n:govoplan-files.token_environment_variable.5af4a79e">
|
||||
<input value={credentialDraft.tokenEnv} disabled={saving} onChange={(event) => patchCredentialDraft({ tokenEnv: event.target.value })} />
|
||||
</FormField>
|
||||
</div>
|
||||
</AdvancedOptionsPanel>
|
||||
</section>
|
||||
|
||||
<section className="adaptive-config-section">
|
||||
@@ -1066,7 +1046,7 @@ export default function FileConnectorSettingsPanel({
|
||||
<h3>Policy</h3>
|
||||
<p>Limit where lower levels may use this credential.</p>
|
||||
</header>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.policy.bb9cf141">
|
||||
<select value={credentialDraft.policyMode} disabled={saving} onChange={(event) => patchCredentialDraft({ policyMode: event.target.value as PolicyMode })}>
|
||||
<option value="allow-provider">i18n:govoplan-files.can_use_this_credential.f4a41971</option>
|
||||
@@ -1118,7 +1098,7 @@ export default function FileConnectorSettingsPanel({
|
||||
<h3>Connection</h3>
|
||||
<p>Name the file server connection and choose the provider.</p>
|
||||
</header>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.profile_id.25961d68">
|
||||
<input className={!editingProfileId && !draft.id.trim() ? "field-input-missing" : undefined} aria-invalid={!editingProfileId && !draft.id.trim() || undefined} value={draft.id} disabled={Boolean(editingProfileId) || saving} onChange={(event) => patchDraft({ id: event.target.value })} placeholder={`${scopeType}-webdav`} />
|
||||
</FormField>
|
||||
@@ -1151,7 +1131,7 @@ export default function FileConnectorSettingsPanel({
|
||||
<h3>Location and credentials</h3>
|
||||
<p>Only fields relevant for the selected provider and credential mode are shown.</p>
|
||||
</header>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.endpoint_url.65aaaa45">
|
||||
<input value={draft.endpointUrl} disabled={saving} onChange={(event) => patchDraft({ endpointUrl: event.target.value })} placeholder={ENDPOINT_PLACEHOLDERS[draft.provider]} />
|
||||
</FormField>
|
||||
@@ -1188,7 +1168,7 @@ export default function FileConnectorSettingsPanel({
|
||||
</DismissibleAlert>
|
||||
}
|
||||
<AdvancedOptionsPanel title="Protocol and compatibility options" summary="Change these only when the provider or network requires an override.">
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.browse_protocol.d1f27c90">
|
||||
<select value={draft.browseProtocol} disabled={saving} onChange={(event) => patchDraft({ browseProtocol: event.target.value })}>
|
||||
<option value="">i18n:govoplan-files.native_default.ba32f7b6</option>
|
||||
@@ -1222,7 +1202,7 @@ export default function FileConnectorSettingsPanel({
|
||||
<ToggleSwitch label="i18n:govoplan-files.import.d6fbc9d2" checked={draft.canImport} disabled={saving} onChange={(canImport) => patchDraft({ canImport })} />
|
||||
<ToggleSwitch label="i18n:govoplan-files.sync.905f6309" checked={draft.canSync} disabled={saving} onChange={(canSync) => patchDraft({ canSync })} />
|
||||
</div>
|
||||
<div className="form-grid two-column-form-grid">
|
||||
<div className="form-grid two">
|
||||
<FormField label="i18n:govoplan-files.policy.bb9cf141">
|
||||
<select value={draft.policyMode} disabled={saving} onChange={(event) => patchDraft({ policyMode: event.target.value as PolicyMode })}>
|
||||
<option value="allow-provider">i18n:govoplan-files.can_use_this_connection.5091a74c</option>
|
||||
@@ -1373,8 +1353,6 @@ function emptyDraft(scopeType: ConnectorScope): ConnectorProfileDraft {
|
||||
username: "",
|
||||
password: "",
|
||||
token: "",
|
||||
passwordEnv: "",
|
||||
tokenEnv: "",
|
||||
secretRef: "",
|
||||
canBrowse: true,
|
||||
canImport: true,
|
||||
@@ -1437,8 +1415,6 @@ function emptyCredentialDraft(scopeType: ConnectorScope): ConnectorCredentialDra
|
||||
username: "",
|
||||
password: "",
|
||||
token: "",
|
||||
passwordEnv: "",
|
||||
tokenEnv: "",
|
||||
secretRef: "",
|
||||
policyMode: "allow-provider",
|
||||
allowedPaths: "",
|
||||
|
||||
Reference in New Issue
Block a user