mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-18 15:22:31 +00:00
fix(setup-wizard): address review — icon, CSS scope, E2E coverage
- Use a plain emoji glyph for the password visibility toggle instead of embedded SVG paths, matching the same pragmatic approach already used for the browser build's menu icons in this codebase. - Scope the wider label width to the E2EE dialogue instead of changing it for every InputRow in every Svelte dialogue. - Extend the existing CouchDB manual setup E2E workflow to assert the passphrase and Obfuscate Properties fields are hidden until encryption is enabled, then appear, and that the passphrase value survives toggling its visibility.
This commit is contained in:
@@ -48,12 +48,13 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DialogHeader title={translateMessage("End-to-End Encryption")} />
|
<div class="sls-e2ee-dialog">
|
||||||
<Guidance>{translateMessage("Please configure your end-to-end encryption settings.")}</Guidance>
|
<DialogHeader title={translateMessage("End-to-End Encryption")} />
|
||||||
<InputRow label={translateMessage("End-to-End Encryption")}>
|
<Guidance>{translateMessage("Please configure your end-to-end encryption settings.")}</Guidance>
|
||||||
|
<InputRow label={translateMessage("End-to-End Encryption")}>
|
||||||
<input type="checkbox" bind:checked={encryptionSettings.encrypt} />
|
<input type="checkbox" bind:checked={encryptionSettings.encrypt} />
|
||||||
</InputRow>
|
</InputRow>
|
||||||
<InfoNote title={translateMessage("Strongly Recommended")}>
|
<InfoNote title={translateMessage("Strongly Recommended")}>
|
||||||
{translateMessage(
|
{translateMessage(
|
||||||
"Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices."
|
"Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices."
|
||||||
)}
|
)}
|
||||||
@@ -61,8 +62,8 @@
|
|||||||
{translateMessage(
|
{translateMessage(
|
||||||
"Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future."
|
"Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future."
|
||||||
)}
|
)}
|
||||||
</InfoNote>
|
</InfoNote>
|
||||||
{#if encryptionSettings.encrypt}
|
{#if encryptionSettings.encrypt}
|
||||||
<InputRow label={translateMessage("Passphrase")}>
|
<InputRow label={translateMessage("Passphrase")}>
|
||||||
<Password
|
<Password
|
||||||
name="e2ee-passphrase"
|
name="e2ee-passphrase"
|
||||||
@@ -84,9 +85,9 @@
|
|||||||
"Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data."
|
"Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data."
|
||||||
)}
|
)}
|
||||||
</InfoNote>
|
</InfoNote>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<ExtraItems title={translateMessage("Advanced")}>
|
<ExtraItems title={translateMessage("Advanced")}>
|
||||||
<InputRow label={translateMessage("Encryption Algorithm")}>
|
<InputRow label={translateMessage("Encryption Algorithm")}>
|
||||||
<select bind:value={encryptionSettings.E2EEAlgorithm} disabled={!encryptionSettings.encrypt}>
|
<select bind:value={encryptionSettings.E2EEAlgorithm} disabled={!encryptionSettings.encrypt}>
|
||||||
{#each Object.values(E2EEAlgorithms) as alg}
|
{#each Object.values(E2EEAlgorithms) as alg}
|
||||||
@@ -105,9 +106,9 @@
|
|||||||
"Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data."
|
"Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data."
|
||||||
)}
|
)}
|
||||||
</InfoNote>
|
</InfoNote>
|
||||||
</ExtraItems>
|
</ExtraItems>
|
||||||
|
|
||||||
<InfoNote warning>
|
<InfoNote warning>
|
||||||
<p>
|
<p>
|
||||||
{translateMessage(
|
{translateMessage(
|
||||||
"Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data."
|
"Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data."
|
||||||
@@ -119,9 +120,22 @@
|
|||||||
)} <br /><br />
|
)} <br /><br />
|
||||||
{translateMessage("Please understand that this is intended behaviour.")}
|
{translateMessage("Please understand that this is intended behaviour.")}
|
||||||
</p>
|
</p>
|
||||||
</InfoNote>
|
</InfoNote>
|
||||||
|
|
||||||
<UserDecisions>
|
<UserDecisions>
|
||||||
<Decision title={translateMessage("Proceed")} important disabled={!e2eeValid} commit={() => commit()} />
|
<Decision title={translateMessage("Proceed")} important disabled={!e2eeValid} commit={() => commit()} />
|
||||||
<Decision title={translateMessage("Cancel")} commit={() => setResult(TYPE_CANCELLED)} />
|
<Decision title={translateMessage("Cancel")} commit={() => setResult(TYPE_CANCELLED)} />
|
||||||
</UserDecisions>
|
</UserDecisions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sls-e2ee-dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
:global(.dialog-host .sls-e2ee-dialog label > span) {
|
||||||
|
width: auto;
|
||||||
|
min-width: 8em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
.dialog-host :global(label > span) {
|
.dialog-host :global(label > span) {
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 8em;
|
width: 8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-host :global(.note),
|
.dialog-host :global(.note),
|
||||||
|
|||||||
@@ -41,37 +41,7 @@
|
|||||||
{disabled}
|
{disabled}
|
||||||
onclick={() => (showPassword = !showPassword)}
|
onclick={() => (showPassword = !showPassword)}
|
||||||
>
|
>
|
||||||
{#if showPassword}
|
👁️
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24" />
|
|
||||||
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" />
|
|
||||||
<path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" />
|
|
||||||
<line x1="2" x2="22" y1="2" y2="22" />
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
||||||
<circle cx="12" cy="12" r="3" />
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -85,11 +55,10 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: var(--text-muted);
|
line-height: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.sls-password-toggle:hover {
|
.sls-password-toggle:hover {
|
||||||
color: var(--text-normal);
|
|
||||||
background: var(--background-modifier-hover);
|
background: var(--background-modifier-hover);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -117,19 +117,55 @@ async function enterManualCouchDBSettings(port: number, couchDb: CouchDbConfig,
|
|||||||
|
|
||||||
const encryption = modalByTitle(page, "End-to-End Encryption");
|
const encryption = modalByTitle(page, "End-to-End Encryption");
|
||||||
await encryption.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
await encryption.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
|
assertEqual(
|
||||||
|
await encryption.locator('input[name="e2ee-passphrase"]').count(),
|
||||||
|
0,
|
||||||
|
"The passphrase field was present before end-to-end encryption was enabled."
|
||||||
|
);
|
||||||
|
assertEqual(
|
||||||
|
await encryption.locator("label.row").filter({ hasText: "Obfuscate Properties" }).count(),
|
||||||
|
0,
|
||||||
|
"The Obfuscate Properties row was present before end-to-end encryption was enabled."
|
||||||
|
);
|
||||||
await encryption
|
await encryption
|
||||||
.locator("label.row")
|
.locator("label.row")
|
||||||
.filter({ hasText: "End-to-End Encryption" })
|
.filter({ hasText: "End-to-End Encryption" })
|
||||||
.locator('input[type="checkbox"]')
|
.locator('input[type="checkbox"]')
|
||||||
.first()
|
.first()
|
||||||
.check({ timeout: uiTimeoutMs });
|
.check({ timeout: uiTimeoutMs });
|
||||||
|
const passphraseInput = encryption.locator('input[name="e2ee-passphrase"]');
|
||||||
|
await passphraseInput.waitFor({ state: "visible", timeout: uiTimeoutMs });
|
||||||
await encryption
|
await encryption
|
||||||
.locator("label.row")
|
.locator("label.row")
|
||||||
.filter({ hasText: "Obfuscate Properties" })
|
.filter({ hasText: "Obfuscate Properties" })
|
||||||
.locator('input[type="checkbox"]')
|
.locator('input[type="checkbox"]')
|
||||||
.first()
|
.first()
|
||||||
.check({ timeout: uiTimeoutMs });
|
.check({ timeout: uiTimeoutMs });
|
||||||
await encryption.locator('input[name="e2ee-passphrase"]').fill(randomBytes(24).toString("base64url"));
|
const passphraseValue = randomBytes(24).toString("base64url");
|
||||||
|
await passphraseInput.fill(passphraseValue);
|
||||||
|
const passwordToggle = encryption.locator("button.sls-password-toggle");
|
||||||
|
await passwordToggle.click({ timeout: uiTimeoutMs });
|
||||||
|
assertEqual(
|
||||||
|
await passphraseInput.getAttribute("type"),
|
||||||
|
"text",
|
||||||
|
"Toggling visibility did not reveal the passphrase."
|
||||||
|
);
|
||||||
|
assertEqual(
|
||||||
|
await passphraseInput.inputValue(),
|
||||||
|
passphraseValue,
|
||||||
|
"Toggling visibility changed the passphrase value."
|
||||||
|
);
|
||||||
|
await passwordToggle.click({ timeout: uiTimeoutMs });
|
||||||
|
assertEqual(
|
||||||
|
await passphraseInput.getAttribute("type"),
|
||||||
|
"password",
|
||||||
|
"Toggling visibility again did not re-mask the passphrase."
|
||||||
|
);
|
||||||
|
assertEqual(
|
||||||
|
await passphraseInput.inputValue(),
|
||||||
|
passphraseValue,
|
||||||
|
"Re-masking the passphrase changed its value."
|
||||||
|
);
|
||||||
});
|
});
|
||||||
screenshots.push(await captureGuideDialogue(port, "guide-couchdb-manual-encryption.png", "End-to-End Encryption"));
|
screenshots.push(await captureGuideDialogue(port, "guide-couchdb-manual-encryption.png", "End-to-End Encryption"));
|
||||||
await withObsidianPage(port, async (page) => {
|
await withObsidianPage(port, async (page) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user