mounts: take an SMB source's username from its entry of sources, not a setting of its own
The live module check refused smb-users: a key naming a user is the operator's value and may have no default, and a setting without one would refuse to compose mounts on every node until set. The username now goes in the source's URL (smb://<user>@<host>/<share>), percent-decoded; a password there is refused unsaid, and a source without a user waits for sources (hq ADR 0283, issue 386).
This commit is contained in:
@@ -149,9 +149,9 @@ func (f *fakeSystem) everything(except ...string) string {
|
||||
}
|
||||
|
||||
const config = "shares=library=/srv/library downloads=/srv/downloads:ro\n" +
|
||||
"sources=games=smb://192.0.2.76/Share@/mnt/games usb=disk:1234-ABCD@/mnt/usb\n" +
|
||||
"sources=games=smb://root@192.0.2.76/Share@/mnt/games usb=disk:1234-ABCD@/mnt/usb\n" +
|
||||
"server=homeserver\nat=192.0.2.10\nnode=workstation\naccount=operator\n" +
|
||||
"smb-users=games=root\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n"
|
||||
"passwords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n"
|
||||
|
||||
func exportsAnswer(t *testing.T, shares ...ServerShare) string {
|
||||
raw, err := json.Marshal(ExportsAnswer{Server: "homeserver", At: "192.0.2.10", Shares: shares})
|
||||
@@ -365,18 +365,18 @@ func TestASilentServerRemovesNoMount(t *testing.T) {
|
||||
|
||||
func TestASourceWithoutItsLoginWaitsForTheOperatorAndIsNotMounted(t *testing.T) {
|
||||
for name, c := range map[string]struct {
|
||||
users, password string
|
||||
writePassword bool
|
||||
waitsFor []string
|
||||
user, password string
|
||||
writePassword bool
|
||||
waitsFor []string
|
||||
}{
|
||||
"no username": {"", testPassword, true, []string{"setting smb-users"}},
|
||||
"no password": {"games=root", "", false, []string{"secret smb-password-games"}},
|
||||
"an empty password file": {"games=root", "", true, []string{"secret smb-password-games"}},
|
||||
"a line break only": {"games=root", "\n", true, []string{"secret smb-password-games"}},
|
||||
"neither": {"other=x", "", false, []string{"setting smb-users", "secret smb-password-games"}},
|
||||
"no username": {"", testPassword, true, []string{"setting sources"}},
|
||||
"no password": {"root@", "", false, []string{"secret smb-password-games"}},
|
||||
"an empty password file": {"root@", "", true, []string{"secret smb-password-games"}},
|
||||
"a line break only": {"root@", "\n", true, []string{"secret smb-password-games"}},
|
||||
"neither": {"", "", false, []string{"setting sources", "secret smb-password-games"}},
|
||||
} {
|
||||
f := newSystem(t)
|
||||
f.write(ConfigFile, "sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users="+c.users+"\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
f.write(ConfigFile, "sources=games=smb://"+c.user+"192.0.2.76/Share@/mnt/games\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
if c.writePassword {
|
||||
f.write("/var/lib/mesh/mounts/smb-password-games.secret", c.password)
|
||||
}
|
||||
@@ -426,7 +426,7 @@ func TestAPasswordIsOneLineKeptAsTyped(t *testing.T) {
|
||||
t.Errorf("two lines are refused, saying nothing typed: %v", err)
|
||||
}
|
||||
f := newSystem(t)
|
||||
f.write(ConfigFile, "sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
f.write(ConfigFile, "sources=games=smb://root@192.0.2.76/Share@/mnt/games\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
f.write("/var/lib/mesh/mounts/smb-password-games.secret", pw+"\n")
|
||||
f.applier(time.Now()).Look(context.Background())
|
||||
creds := filepath.Join(f.root, "/var/lib/mesh/mounts/smb-games.credentials")
|
||||
@@ -449,13 +449,34 @@ func TestAPasswordIsOneLineKeptAsTyped(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheUsersSettingIsReadAndWhatCannotBeIsSaid(t *testing.T) {
|
||||
users, problems := ParseSMBUsers("games=root nas=media-user bad Nas=x nas=again weird=a=b")
|
||||
if users["games"] != "root" || users["nas"] != "media-user" || len(users) != 2 || len(problems) != 4 {
|
||||
func TestAnSMBSourcesUserIsInItsEntryAndAPasswordThereIsRefusedUnsaid(t *testing.T) {
|
||||
sources, problems := ParseSources("a=smb://root@192.0.2.76/Share@/mnt/a b=smb://192.0.2.76/Share@/mnt/b " +
|
||||
"c=smb://WORKGROUP%5Cme@nas/Media@/mnt/c d=smb://me%40example@nas/Media@/mnt/d:ro")
|
||||
users := map[string]string{}
|
||||
for _, s := range sources {
|
||||
users[s.Name] = s.User
|
||||
if s.Host == "" || s.Share == "" {
|
||||
t.Errorf("%s: %+v", s.Name, s)
|
||||
}
|
||||
}
|
||||
if len(problems) != 0 || users["a"] != "root" || users["b"] != "" || users["c"] != `WORKGROUP\me` || users["d"] != "me@example" {
|
||||
t.Errorf("users %v problems %q", users, problems)
|
||||
}
|
||||
if users, problems := ParseSMBUsers(None); len(users) != 0 || len(problems) != 0 {
|
||||
t.Errorf("none: %v %q", users, problems)
|
||||
for what, entry := range map[string]string{
|
||||
"a password in the URL": "e=smb://root:" + testPassword + "@nas/Media@/mnt/e",
|
||||
"a password with a slash": "e=smb://root:a/" + testPassword + "@nas/Media@/mnt/e",
|
||||
"a user with a space, encoded": "e=smb://a%20" + testPassword + "@nas/Media@/mnt/e",
|
||||
"a bad percent escape": "e=smb://%zz" + testPassword + "@nas/Media@/mnt/e",
|
||||
"no mount point, with a password": "smb://root:" + testPassword + "@nas/Media",
|
||||
} {
|
||||
got, problems := ParseSources(entry)
|
||||
said := strings.Join(problems, " ")
|
||||
if len(got) != 0 || len(problems) != 1 || strings.Contains(said, testPassword) {
|
||||
t.Errorf("%s: %+v %q", what, got, said)
|
||||
}
|
||||
}
|
||||
if _, problems := ParseSources("e=smb://root:" + testPassword + "@nas/Media@/mnt/e"); !strings.Contains(problems[0], "smb-password-<source>") {
|
||||
t.Errorf("a password in the URL is said with where it goes: %q", problems)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,7 +708,7 @@ func TestAWaitBesideAFaultIsUnhealthy(t *testing.T) {
|
||||
func TestAPasswordThatCannotBeReadIsAFaultSaidWithoutValueOrPath(t *testing.T) {
|
||||
// A directory where the file should be: read fails, and not as "not there" (as root a mode would not stop it).
|
||||
f := newSystem(t)
|
||||
f.write(ConfigFile, "sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
f.write(ConfigFile, "sources=games=smb://root@192.0.2.76/Share@/mnt/games\npasswords=/var/lib/mesh/mounts\nstate=/var/lib/mesh/mounts\n")
|
||||
f.write("/var/lib/mesh/mounts/smb-password-games.secret/inside", testPassword)
|
||||
st := f.applier(time.Now()).Look(context.Background())
|
||||
if len(st.Shares) != 1 || st.Shares[0].State != StateFailed || !strings.Contains(st.Shares[0].Why, "could not be read") ||
|
||||
@@ -697,8 +718,8 @@ func TestAPasswordThatCannotBeReadIsAFaultSaidWithoutValueOrPath(t *testing.T) {
|
||||
|
||||
// No passwords= and no state=: the source is not set up, and no relative path is read.
|
||||
for _, cfg := range []string{
|
||||
"sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\n",
|
||||
"sources=games=smb://192.0.2.76/Share@/mnt/games\nsmb-users=games=root\npasswords=relative/dir\n",
|
||||
"sources=games=smb://root@192.0.2.76/Share@/mnt/games\n",
|
||||
"sources=games=smb://root@192.0.2.76/Share@/mnt/games\npasswords=relative/dir\n",
|
||||
} {
|
||||
g := newSystem(t)
|
||||
g.write(ConfigFile, cfg)
|
||||
|
||||
Reference in New Issue
Block a user