hide tls certificate fields for backends that never use tls

This commit is contained in:
Jarek Krochmalski committed 2026-08-25 05:42:29 +02:00
1 parent 730ad8ff82
commit d5d979aa5d
3 files changed
+60 -5

No files matched your search

+16
View File
@@ -13,6 +13,22 @@ export function isLocalRepo(repository: string): boolean {
return repository.startsWith('/') || repository.startsWith('./');
}
/**
* Whether a backup destination's TLS-certificate fields (CA / client cert) are
* meaningful for a given backend type. Only backends that can be self-hosted over
* HTTPS with a private/self-signed CA qualify: `s3` (MinIO/Ceph) and `rest`. A
* local path has no network, and `b2`/`azure`/`gs` are managed clouds served from
* public CAs, so the TLS fields are noise there. When editing a destination that
* already has a cert stored, return true regardless so its data is never hidden.
*/
export function backendSupportsTls(
backendType: string,
opts?: { isEditing?: boolean; hasStoredCert?: boolean }
): boolean {
if (backendType === 's3' || backendType === 'rest') return true;
return !!(opts?.isEditing && opts?.hasStoredCert);
}
/** An environment reachable over the network (hawser or direct-with-host). */
export function isRemoteEnvironment(env?: { connectionType?: string | null; host?: string | null } | null): boolean {
if (!env) return false;
@@ -18,6 +18,7 @@
import { FieldLabel } from '$lib/components/ui/field-label';
import { toast } from 'svelte-sonner';
import { focusFirstInput } from '$lib/utils';
import { backendSupportsTls } from '$lib/shared/repo-predicates';
interface Destination {
id: number;
@@ -265,6 +266,12 @@
const repoFields = $derived(selectedBackend.fields.filter(f => !f.envKey));
const credentialFields = $derived(selectedBackend.fields.filter(f => f.envKey));
// TLS certs only apply to backends self-hostable over HTTPS with a private CA
// (S3/REST); hidden for local/B2/Azure/Google Cloud. See backendSupportsTls.
const showTlsSection = $derived(
backendSupportsTls(formBackendType, { isEditing, hasStoredCert: hadCacert || hadTlsClientCert })
);
// A field is mandatory unless it opts out (optional) or is a toggle (skipHostKey).
function fieldRequired(f: FormField): boolean {
return !f.optional && f.key !== 'skipHostKey';
@@ -727,10 +734,11 @@
</div>
</div>
<!-- TLS certificates: for any backend that speaks TLS with a private CA (self-hosted
rest-server, MinIO/S3, rclone-over-https). Optional; blank = use the system trust
store, no client cert. Shown for all backend types (someone may run MinIO with a
private CA). PEM content, uploaded or pasted. -->
<!-- TLS certificates: only for backends that can be self-hosted over HTTPS with a
private CA - S3 (MinIO/Ceph) and a REST server. A local path has no network, and
B2/Azure/Google Cloud are managed clouds with public CAs, so the section is hidden
there. Optional; blank = use the system trust store. PEM content, uploaded or pasted. -->
{#if showTlsSection}
<div class="border-t pt-3 mt-2 space-y-3">
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">TLS certificates (optional)</span>
<p class="text-xs text-muted-foreground -mt-1">
@@ -783,6 +791,7 @@
</div>
</div>
</div>
{/if}
<!-- Policies section -->
+31 -1
View File
@@ -6,7 +6,7 @@
* now advisory-only for the UI hint.
*/
import { describe, test, expect } from 'bun:test';
import { isLocalRepo, isRemoteEnvironment, localRepoNeedsSameHost } from '../src/lib/shared/repo-predicates';
import { isLocalRepo, isRemoteEnvironment, localRepoNeedsSameHost, backendSupportsTls } from '../src/lib/shared/repo-predicates';
describe('isLocalRepo', () => {
test('absolute and relative filesystem paths are local', () => {
@@ -55,3 +55,33 @@ describe('localRepoNeedsSameHost (#1316 advisory)', () => {
expect(localRepoNeedsSameHost(localDest, undefined)).toBe(false);
});
});
describe('backendSupportsTls (destination TLS fields visibility)', () => {
test('s3 and rest support TLS (self-hostable over HTTPS with a private CA)', () => {
expect(backendSupportsTls('s3')).toBe(true);
expect(backendSupportsTls('rest')).toBe(true);
});
test('local path and managed clouds do NOT show TLS fields', () => {
expect(backendSupportsTls('local')).toBe(false);
expect(backendSupportsTls('b2')).toBe(false);
expect(backendSupportsTls('azure')).toBe(false);
expect(backendSupportsTls('gs')).toBe(false);
});
test('editing a non-TLS backend that already has a cert stored still shows the section', () => {
// A destination created before this filter that carries a CA must not have its
// stored cert hidden on edit.
expect(backendSupportsTls('gs', { isEditing: true, hasStoredCert: true })).toBe(true);
expect(backendSupportsTls('local', { isEditing: true, hasStoredCert: true })).toBe(true);
});
test('editing without a stored cert keeps the section hidden for non-TLS backends', () => {
expect(backendSupportsTls('gs', { isEditing: true, hasStoredCert: false })).toBe(false);
expect(backendSupportsTls('azure', { isEditing: true, hasStoredCert: false })).toBe(false);
});
test('creating (not editing) never shows TLS for a non-TLS backend regardless of flags', () => {
expect(backendSupportsTls('local', { isEditing: false, hasStoredCert: true })).toBe(false);
});
});