github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/config/config_test.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "testing" 9 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/config/obscure" 12 "github.com/rclone/rclone/fs/rc" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func testConfigFile(t *testing.T, configFileName string) func() { 18 configKey = nil // reset password 19 _ = os.Unsetenv("_RCLONE_CONFIG_KEY_FILE") 20 _ = os.Unsetenv("RCLONE_CONFIG_PASS") 21 // create temp config file 22 tempFile, err := ioutil.TempFile("", configFileName) 23 assert.NoError(t, err) 24 path := tempFile.Name() 25 assert.NoError(t, tempFile.Close()) 26 27 // temporarily adapt configuration 28 oldOsStdout := os.Stdout 29 oldConfigPath := ConfigPath 30 oldConfig := fs.Config 31 oldConfigFile := configFile 32 oldReadLine := ReadLine 33 oldPassword := Password 34 os.Stdout = nil 35 ConfigPath = path 36 fs.Config = &fs.ConfigInfo{} 37 configFile = nil 38 39 LoadConfig() 40 assert.Equal(t, []string{}, getConfigData().GetSectionList()) 41 42 // Fake a remote 43 fs.Register(&fs.RegInfo{ 44 Name: "config_test_remote", 45 Options: fs.Options{ 46 { 47 Name: "bool", 48 Default: false, 49 IsPassword: false, 50 }, 51 { 52 Name: "pass", 53 Default: "", 54 IsPassword: true, 55 }, 56 }, 57 }) 58 59 // Undo the above 60 return func() { 61 err := os.Remove(path) 62 assert.NoError(t, err) 63 64 os.Stdout = oldOsStdout 65 ConfigPath = oldConfigPath 66 ReadLine = oldReadLine 67 Password = oldPassword 68 fs.Config = oldConfig 69 configFile = oldConfigFile 70 71 _ = os.Unsetenv("_RCLONE_CONFIG_KEY_FILE") 72 _ = os.Unsetenv("RCLONE_CONFIG_PASS") 73 } 74 } 75 76 // makeReadLine makes a simple readLine which returns a fixed list of 77 // strings 78 func makeReadLine(answers []string) func() string { 79 i := 0 80 return func() string { 81 i = i + 1 82 return answers[i-1] 83 } 84 } 85 86 func TestCRUD(t *testing.T) { 87 defer testConfigFile(t, "crud.conf")() 88 89 // script for creating remote 90 ReadLine = makeReadLine([]string{ 91 "config_test_remote", // type 92 "true", // bool value 93 "y", // type my own password 94 "secret", // password 95 "secret", // repeat 96 "y", // looks good, save 97 }) 98 NewRemote("test") 99 100 assert.Equal(t, []string{"test"}, configFile.GetSectionList()) 101 assert.Equal(t, "config_test_remote", FileGet("test", "type")) 102 assert.Equal(t, "true", FileGet("test", "bool")) 103 assert.Equal(t, "secret", obscure.MustReveal(FileGet("test", "pass"))) 104 105 // normal rename, test → asdf 106 ReadLine = makeReadLine([]string{ 107 "asdf", 108 "asdf", 109 "asdf", 110 }) 111 RenameRemote("test") 112 113 assert.Equal(t, []string{"asdf"}, configFile.GetSectionList()) 114 assert.Equal(t, "config_test_remote", FileGet("asdf", "type")) 115 assert.Equal(t, "true", FileGet("asdf", "bool")) 116 assert.Equal(t, "secret", obscure.MustReveal(FileGet("asdf", "pass"))) 117 118 // delete remote 119 DeleteRemote("asdf") 120 assert.Equal(t, []string{}, configFile.GetSectionList()) 121 } 122 123 func TestChooseOption(t *testing.T) { 124 defer testConfigFile(t, "crud.conf")() 125 126 // script for creating remote 127 ReadLine = makeReadLine([]string{ 128 "config_test_remote", // type 129 "false", // bool value 130 "x", // bad choice 131 "g", // generate password 132 "1024", // very big 133 "y", // password OK 134 "y", // looks good, save 135 }) 136 Password = func(bits int) (string, error) { 137 assert.Equal(t, 1024, bits) 138 return "not very random password", nil 139 } 140 NewRemote("test") 141 142 assert.Equal(t, "false", FileGet("test", "bool")) 143 assert.Equal(t, "not very random password", obscure.MustReveal(FileGet("test", "pass"))) 144 145 // script for creating remote 146 ReadLine = makeReadLine([]string{ 147 "config_test_remote", // type 148 "true", // bool value 149 "n", // not required 150 "y", // looks good, save 151 }) 152 NewRemote("test") 153 154 assert.Equal(t, "true", FileGet("test", "bool")) 155 assert.Equal(t, "", FileGet("test", "pass")) 156 } 157 158 func TestNewRemoteName(t *testing.T) { 159 defer testConfigFile(t, "crud.conf")() 160 161 // script for creating remote 162 ReadLine = makeReadLine([]string{ 163 "config_test_remote", // type 164 "true", // bool value 165 "n", // not required 166 "y", // looks good, save 167 }) 168 NewRemote("test") 169 170 ReadLine = makeReadLine([]string{ 171 "test", // already exists 172 "", // empty string not allowed 173 "bad@characters", // bad characters 174 "newname", // OK 175 }) 176 177 assert.Equal(t, "newname", NewRemoteName()) 178 } 179 180 func TestCreateUpatePasswordRemote(t *testing.T) { 181 defer testConfigFile(t, "update.conf")() 182 183 for _, doObscure := range []bool{false, true} { 184 for _, noObscure := range []bool{false, true} { 185 if doObscure && noObscure { 186 break 187 } 188 t.Run(fmt.Sprintf("doObscure=%v,noObscure=%v", doObscure, noObscure), func(t *testing.T) { 189 require.NoError(t, CreateRemote("test2", "config_test_remote", rc.Params{ 190 "bool": true, 191 "pass": "potato", 192 }, doObscure, noObscure)) 193 194 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 195 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 196 assert.Equal(t, "true", FileGet("test2", "bool")) 197 gotPw := FileGet("test2", "pass") 198 if !noObscure { 199 gotPw = obscure.MustReveal(gotPw) 200 } 201 assert.Equal(t, "potato", gotPw) 202 203 wantPw := obscure.MustObscure("potato2") 204 require.NoError(t, UpdateRemote("test2", rc.Params{ 205 "bool": false, 206 "pass": wantPw, 207 "spare": "spare", 208 }, doObscure, noObscure)) 209 210 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 211 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 212 assert.Equal(t, "false", FileGet("test2", "bool")) 213 gotPw = FileGet("test2", "pass") 214 if doObscure { 215 gotPw = obscure.MustReveal(gotPw) 216 } 217 assert.Equal(t, wantPw, gotPw) 218 219 require.NoError(t, PasswordRemote("test2", rc.Params{ 220 "pass": "potato3", 221 })) 222 223 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 224 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 225 assert.Equal(t, "false", FileGet("test2", "bool")) 226 assert.Equal(t, "potato3", obscure.MustReveal(FileGet("test2", "pass"))) 227 }) 228 } 229 } 230 231 } 232 233 // Test some error cases 234 func TestReveal(t *testing.T) { 235 for _, test := range []struct { 236 in string 237 wantErr string 238 }{ 239 {"YmJiYmJiYmJiYmJiYmJiYp*gcEWbAw", "base64 decode failed when revealing password - is it obscured?: illegal base64 data at input byte 22"}, 240 {"aGVsbG8", "input too short when revealing password - is it obscured?"}, 241 {"", "input too short when revealing password - is it obscured?"}, 242 } { 243 gotString, gotErr := obscure.Reveal(test.in) 244 assert.Equal(t, "", gotString) 245 assert.Equal(t, test.wantErr, gotErr.Error()) 246 } 247 } 248 249 func TestConfigLoad(t *testing.T) { 250 oldConfigPath := ConfigPath 251 ConfigPath = "./testdata/plain.conf" 252 defer func() { 253 ConfigPath = oldConfigPath 254 }() 255 configKey = nil // reset password 256 c, err := loadConfigFile() 257 if err != nil { 258 t.Fatal(err) 259 } 260 sections := c.GetSectionList() 261 var expect = []string{"RCLONE_ENCRYPT_V0", "nounc", "unc"} 262 assert.Equal(t, expect, sections) 263 264 keys := c.GetKeyList("nounc") 265 expect = []string{"type", "nounc"} 266 assert.Equal(t, expect, keys) 267 } 268 269 func TestConfigLoadEncrypted(t *testing.T) { 270 var err error 271 oldConfigPath := ConfigPath 272 ConfigPath = "./testdata/encrypted.conf" 273 defer func() { 274 ConfigPath = oldConfigPath 275 configKey = nil // reset password 276 }() 277 278 // Set correct password 279 err = setConfigPassword("asdf") 280 require.NoError(t, err) 281 c, err := loadConfigFile() 282 require.NoError(t, err) 283 sections := c.GetSectionList() 284 var expect = []string{"nounc", "unc"} 285 assert.Equal(t, expect, sections) 286 287 keys := c.GetKeyList("nounc") 288 expect = []string{"type", "nounc"} 289 assert.Equal(t, expect, keys) 290 } 291 292 func TestConfigLoadEncryptedWithValidPassCommand(t *testing.T) { 293 oldConfigPath := ConfigPath 294 oldConfig := fs.Config 295 ConfigPath = "./testdata/encrypted.conf" 296 // using fs.Config.PasswordCommand, correct password 297 fs.Config.PasswordCommand = fs.SpaceSepList{"echo", "asdf"} 298 defer func() { 299 ConfigPath = oldConfigPath 300 configKey = nil // reset password 301 fs.Config = oldConfig 302 fs.Config.PasswordCommand = nil 303 }() 304 305 configKey = nil // reset password 306 307 c, err := loadConfigFile() 308 require.NoError(t, err) 309 310 sections := c.GetSectionList() 311 var expect = []string{"nounc", "unc"} 312 assert.Equal(t, expect, sections) 313 314 keys := c.GetKeyList("nounc") 315 expect = []string{"type", "nounc"} 316 assert.Equal(t, expect, keys) 317 } 318 319 func TestConfigLoadEncryptedWithInvalidPassCommand(t *testing.T) { 320 oldConfigPath := ConfigPath 321 oldConfig := fs.Config 322 ConfigPath = "./testdata/encrypted.conf" 323 // using fs.Config.PasswordCommand, incorrect password 324 fs.Config.PasswordCommand = fs.SpaceSepList{"echo", "asdf-blurfl"} 325 defer func() { 326 ConfigPath = oldConfigPath 327 configKey = nil // reset password 328 fs.Config = oldConfig 329 fs.Config.PasswordCommand = nil 330 }() 331 332 configKey = nil // reset password 333 334 _, err := loadConfigFile() 335 require.Error(t, err) 336 assert.Contains(t, err.Error(), "using --password-command derived password") 337 } 338 339 func TestConfigLoadEncryptedFailures(t *testing.T) { 340 var err error 341 342 // This file should be too short to be decoded. 343 oldConfigPath := ConfigPath 344 ConfigPath = "./testdata/enc-short.conf" 345 defer func() { ConfigPath = oldConfigPath }() 346 _, err = loadConfigFile() 347 require.Error(t, err) 348 349 // This file contains invalid base64 characters. 350 ConfigPath = "./testdata/enc-invalid.conf" 351 _, err = loadConfigFile() 352 require.Error(t, err) 353 354 // This file contains invalid base64 characters. 355 ConfigPath = "./testdata/enc-too-new.conf" 356 _, err = loadConfigFile() 357 require.Error(t, err) 358 359 // This file does not exist. 360 ConfigPath = "./testdata/filenotfound.conf" 361 c, err := loadConfigFile() 362 assert.Equal(t, errorConfigFileNotFound, err) 363 assert.Nil(t, c) 364 } 365 366 func TestPassword(t *testing.T) { 367 defer func() { 368 configKey = nil // reset password 369 }() 370 var err error 371 // Empty password should give error 372 err = setConfigPassword(" \t ") 373 require.Error(t, err) 374 375 // Test invalid utf8 sequence 376 err = setConfigPassword(string([]byte{0xff, 0xfe, 0xfd}) + "abc") 377 require.Error(t, err) 378 379 // Simple check of wrong passwords 380 hashedKeyCompare(t, "mis", "match", false) 381 382 // Check that passwords match after unicode normalization 383 hashedKeyCompare(t, "ff\u0041\u030A", "ffÅ", true) 384 385 // Check that passwords preserves case 386 hashedKeyCompare(t, "abcdef", "ABCDEF", false) 387 388 } 389 390 func hashedKeyCompare(t *testing.T, a, b string, shouldMatch bool) { 391 err := setConfigPassword(a) 392 require.NoError(t, err) 393 k1 := configKey 394 395 err = setConfigPassword(b) 396 require.NoError(t, err) 397 k2 := configKey 398 399 if shouldMatch { 400 assert.Equal(t, k1, k2) 401 } else { 402 assert.NotEqual(t, k1, k2) 403 } 404 } 405 406 func TestMatchProvider(t *testing.T) { 407 for _, test := range []struct { 408 config string 409 provider string 410 want bool 411 }{ 412 {"", "", true}, 413 {"one", "one", true}, 414 {"one,two", "two", true}, 415 {"one,two,three", "two", true}, 416 {"one", "on", false}, 417 {"one,two,three", "tw", false}, 418 {"!one,two,three", "two", false}, 419 {"!one,two,three", "four", true}, 420 } { 421 what := fmt.Sprintf("%q,%q", test.config, test.provider) 422 got := matchProvider(test.config, test.provider) 423 assert.Equal(t, test.want, got, what) 424 } 425 } 426 427 func TestFileRefresh(t *testing.T) { 428 defer testConfigFile(t, "refresh.conf")() 429 require.NoError(t, CreateRemote("refresh_test", "config_test_remote", rc.Params{ 430 "bool": true, 431 }, false, false)) 432 b, err := ioutil.ReadFile(ConfigPath) 433 assert.NoError(t, err) 434 435 b = bytes.Replace(b, []byte("refresh_test"), []byte("refreshed_test"), 1) 436 err = ioutil.WriteFile(ConfigPath, b, 0644) 437 assert.NoError(t, err) 438 439 assert.NotEqual(t, []string{"refreshed_test"}, configFile.GetSectionList()) 440 err = FileRefresh() 441 assert.NoError(t, err) 442 assert.Equal(t, []string{"refreshed_test"}, configFile.GetSectionList()) 443 }