From 3937c9d4f6a5fa136bf05f37b85f01b042e15a25 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Thu, 29 Jan 2026 21:44:44 +0100 Subject: [PATCH] feat: add config for SMB guest mode Closes #279 --- app/client/api-client/types.gen.ts | 50 +++++++++++-------- .../components/volume-forms/smb-form.tsx | 43 +++++++++++++++- app/schemas/volumes.ts | 5 +- .../modules/backends/smb/smb-backend.ts | 17 +++++-- app/server/modules/volumes/volume.service.ts | 2 +- 5 files changed, 87 insertions(+), 30 deletions(-) diff --git a/app/client/api-client/types.gen.ts b/app/client/api-client/types.gen.ts index 6b706223..73c55985 100644 --- a/app/client/api-client/types.gen.ts +++ b/app/client/api-client/types.gen.ts @@ -97,14 +97,15 @@ export type ListVolumesResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -166,14 +167,15 @@ export type CreateVolumeData = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -232,14 +234,15 @@ export type CreateVolumeResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -301,14 +304,15 @@ export type TestConnectionData = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -420,14 +424,15 @@ export type GetVolumeResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -491,14 +496,15 @@ export type UpdateVolumeData = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -566,14 +572,15 @@ export type UpdateVolumeResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -2103,14 +2110,15 @@ export type ListBackupSchedulesResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -2485,14 +2493,15 @@ export type GetBackupScheduleResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; @@ -2848,14 +2857,15 @@ export type GetBackupScheduleForVolumeResponses = { } | { backend: "smb"; - password: string; server: string; share: string; - username: string; vers?: "1.0" | "2.0" | "2.1" | "3.0" | "auto"; port?: number; domain?: string; + guest?: boolean; + password?: string; readOnly?: boolean; + username?: string; } | { backend: "webdav"; diff --git a/app/client/modules/volumes/components/volume-forms/smb-form.tsx b/app/client/modules/volumes/components/volume-forms/smb-form.tsx index b46fcef3..3c9f0167 100644 --- a/app/client/modules/volumes/components/volume-forms/smb-form.tsx +++ b/app/client/modules/volumes/components/volume-forms/smb-form.tsx @@ -17,6 +17,8 @@ type Props = { }; export const SMBForm = ({ form }: Props) => { + const guest = form.watch("guest"); + return ( <> { )} /> + ( + + Guest Mode + +
+ { + field.onChange(e.target.checked); + }} + className="rounded border-gray-300" + /> + Connect as guest (no authentication) +
+
+ + Connect to the SMB share without credentials. Username and password fields will be disabled. + + +
+ )} + /> { Username - + Username for SMB authentication. @@ -68,7 +102,12 @@ export const SMBForm = ({ form }: Props) => { Password - + Password for SMB authentication. diff --git a/app/schemas/volumes.ts b/app/schemas/volumes.ts index 5fab2c51..dd57e713 100644 --- a/app/schemas/volumes.ts +++ b/app/schemas/volumes.ts @@ -24,8 +24,9 @@ export const smbConfigSchema = type({ backend: "'smb'", server: "string", share: "string", - username: "string", - password: "string", + username: "string?", + password: "string?", + guest: "boolean?", vers: type("'1.0' | '2.0' | '2.1' | '3.0' | 'auto'").default("auto"), domain: "string?", port: type("string.integer").or(type("number")).to("1 <= number <= 65535").default(445), diff --git a/app/server/modules/backends/smb/smb-backend.ts b/app/server/modules/backends/smb/smb-backend.ts index 9d4ef48a..0cb85c85 100644 --- a/app/server/modules/backends/smb/smb-backend.ts +++ b/app/server/modules/backends/smb/smb-backend.ts @@ -36,21 +36,28 @@ const mount = async (config: BackendConfig, path: string) => { const run = async () => { await fs.mkdir(path, { recursive: true }); - const password = await cryptoUtils.resolveSecret(config.password); - const source = `//${config.server}/${config.share}`; const { uid, gid } = os.userInfo(); + + const options = [`port=${config.port}`, `uid=${uid}`, `gid=${gid}`]; + const credentialsDir = await fs.mkdtemp(`${os.tmpdir()}/zerobyte-smb-`); const credentialsPath = `${credentialsDir}/credentials`; - const credentialsLines = [`username=${config.username}`, `password=${password}`]; + const credentialsLines: string[] = []; + + if (config.guest) { + credentialsLines.push("username=guest", "password="); + } else { + const password = await cryptoUtils.resolveSecret(config.password ?? ""); + credentialsLines.push(`username=${config.username ?? ""}`, `password=${password}`); + } if (config.domain) { credentialsLines.push(`domain=${config.domain}`); } await fs.writeFile(credentialsPath, credentialsLines.join("\n"), { mode: 0o600 }); - - const options = [`credentials=${credentialsPath}`, `port=${config.port}`, `uid=${uid}`, `gid=${gid}`]; + options.push(`credentials=${credentialsPath}`); if (config.vers && config.vers !== "auto") { options.push(`vers=${config.vers}`); diff --git a/app/server/modules/volumes/volume.service.ts b/app/server/modules/volumes/volume.service.ts index f150c9f5..ef869683 100644 --- a/app/server/modules/volumes/volume.service.ts +++ b/app/server/modules/volumes/volume.service.ts @@ -24,7 +24,7 @@ async function encryptSensitiveFields(config: BackendConfig): Promise