feat(samba): settings registry + smb.conf/compose renderers (R-7 slice 1, Part 1)
SMBSettings + SMBShare registry in settings (password never stored — only UserSet); NetBIOS-safe name validation. Pure infra renderers: RenderSambaConfig (hardened global block: SMB2 floor, bind interfaces only=lo eth0, disable netbios=no, force user block) + RenderSambaCompose (network_mode host, pinned image, :ro bind for read-only shares, passdb volume). Exact smb.conf golden + CRUD/validation tests.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func sampleSambaData() SambaData {
|
||||
return SambaData{
|
||||
ServerName: "FELHOM",
|
||||
UID: 1000,
|
||||
Shares: []SambaShareRender{
|
||||
{Name: "dokumentumok", Path: "/mnt/felhom-drives/scratch1/shares/dokumentumok", ReadOnly: false},
|
||||
{Name: "filmek", Path: "/mnt/felhom-drives/media/filmek", ReadOnly: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Exact golden for smb.conf (§10): the hardened global block + one section per share, in registry
|
||||
// order, with the force-user block. A drift in any managed directive fails here.
|
||||
func TestRenderSambaConfig_Golden(t *testing.T) {
|
||||
const want = `# Samba (LAN network-sharing) — managed by felhom-controller (R-7).
|
||||
# WARNING: auto-generated. Manual edits are overwritten on the next share change.
|
||||
[global]
|
||||
workgroup = WORKGROUP
|
||||
server string = Felhom hálózati megosztás
|
||||
netbios name = FELHOM
|
||||
security = user
|
||||
map to guest = never
|
||||
server min protocol = SMB2
|
||||
disable netbios = no
|
||||
bind interfaces only = yes
|
||||
interfaces = lo eth0
|
||||
smb ports = 445
|
||||
load printers = no
|
||||
printing = bsd
|
||||
printcap name = /dev/null
|
||||
disable spoolss = yes
|
||||
|
||||
[dokumentumok]
|
||||
path = /mnt/felhom-drives/scratch1/shares/dokumentumok
|
||||
read only = no
|
||||
valid users = felhom
|
||||
force user = felhom
|
||||
force group = felhom
|
||||
create mask = 0644
|
||||
directory mask = 0755
|
||||
|
||||
[filmek]
|
||||
path = /mnt/felhom-drives/media/filmek
|
||||
read only = yes
|
||||
valid users = felhom
|
||||
force user = felhom
|
||||
force group = felhom
|
||||
create mask = 0644
|
||||
directory mask = 0755
|
||||
`
|
||||
got := RenderSambaConfig(sampleSambaData())
|
||||
if got != want {
|
||||
t.Errorf("smb.conf golden mismatch.\n--- got ---\n%s\n--- want ---\n%s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderSambaConfig_NoShares(t *testing.T) {
|
||||
got := RenderSambaConfig(SambaData{ServerName: "OTTHON", UID: 1000})
|
||||
if !strings.Contains(got, "netbios name = OTTHON") {
|
||||
t.Error("server name not rendered")
|
||||
}
|
||||
if strings.Count(got, "[") != 1 { // only [global]
|
||||
t.Errorf("no shares should mean only the [global] section:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Compose: host network + pinned image + config :ro + passdb volume, and — the Scenario B core — a
|
||||
// read-only share gets a :ro bind while a writable one does not. Share binds are sorted (filmek<scratch1).
|
||||
func TestRenderSambaCompose(t *testing.T) {
|
||||
got := RenderSambaCompose(sampleSambaData())
|
||||
for _, want := range []string{
|
||||
"network_mode: host",
|
||||
"image: gitea.dooplex.hu/admin/felhom-samba:1.0.0",
|
||||
"- ./smb.conf:/etc/samba/smb.conf:ro",
|
||||
"- samba-passdb:/var/lib/samba",
|
||||
"FELHOM_SERVER_NAME=FELHOM",
|
||||
"FELHOM_UID=1000",
|
||||
} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf("compose missing %q\n%s", want, got)
|
||||
}
|
||||
}
|
||||
// Read-only share → :ro bind (red-proof target: drop the :ro in the renderer and this fails).
|
||||
roBind := "- /mnt/felhom-drives/media/filmek:/mnt/felhom-drives/media/filmek:ro"
|
||||
if !strings.Contains(got, roBind) {
|
||||
t.Errorf("read-only share must get a :ro bind, missing %q\n%s", roBind, got)
|
||||
}
|
||||
// Writable share → plain bind, NOT :ro.
|
||||
rwBind := "- /mnt/felhom-drives/scratch1/shares/dokumentumok:/mnt/felhom-drives/scratch1/shares/dokumentumok\n"
|
||||
if !strings.Contains(got, rwBind) {
|
||||
t.Errorf("writable share must get a plain bind, missing %q\n%s", rwBind, got)
|
||||
}
|
||||
if strings.Contains(got, "/mnt/felhom-drives/scratch1/shares/dokumentumok:/mnt/felhom-drives/scratch1/shares/dokumentumok:ro") {
|
||||
t.Error("writable share must NOT be :ro")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user