github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/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/ncw/rclone/fs" 11 "github.com/ncw/rclone/fs/config/obscure" 12 "github.com/ncw/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 os.Stdout = nil 34 ConfigPath = path 35 fs.Config = &fs.ConfigInfo{} 36 configFile = nil 37 38 LoadConfig() 39 assert.Equal(t, []string{}, getConfigData().GetSectionList()) 40 41 // Fake a remote 42 fs.Register(&fs.RegInfo{ 43 Name: "config_test_remote", 44 Options: fs.Options{ 45 { 46 Name: "bool", 47 Default: false, 48 IsPassword: false, 49 }, 50 { 51 Name: "pass", 52 Default: "", 53 IsPassword: true, 54 }, 55 }, 56 }) 57 58 // Undo the above 59 return func() { 60 err := os.Remove(path) 61 assert.NoError(t, err) 62 63 os.Stdout = oldOsStdout 64 ConfigPath = oldConfigPath 65 ReadLine = oldReadLine 66 fs.Config = oldConfig 67 configFile = oldConfigFile 68 69 _ = os.Unsetenv("_RCLONE_CONFIG_KEY_FILE") 70 _ = os.Unsetenv("RCLONE_CONFIG_PASS") 71 } 72 } 73 74 func TestCRUD(t *testing.T) { 75 defer testConfigFile(t, "crud.conf")() 76 77 // expect script for creating remote 78 i := 0 79 ReadLine = func() string { 80 answers := []string{ 81 "config_test_remote", // type 82 "true", // bool value 83 "y", // type my own password 84 "secret", // password 85 "secret", // repeat 86 "y", // looks good, save 87 } 88 i = i + 1 89 return answers[i-1] 90 } 91 92 NewRemote("test") 93 94 assert.Equal(t, []string{"test"}, configFile.GetSectionList()) 95 assert.Equal(t, "config_test_remote", FileGet("test", "type")) 96 assert.Equal(t, "true", FileGet("test", "bool")) 97 assert.Equal(t, "secret", obscure.MustReveal(FileGet("test", "pass"))) 98 99 // normal rename, test → asdf 100 ReadLine = func() string { return "asdf" } 101 RenameRemote("test") 102 103 assert.Equal(t, []string{"asdf"}, configFile.GetSectionList()) 104 assert.Equal(t, "config_test_remote", FileGet("asdf", "type")) 105 assert.Equal(t, "true", FileGet("asdf", "bool")) 106 assert.Equal(t, "secret", obscure.MustReveal(FileGet("asdf", "pass"))) 107 108 // no-op rename, asdf → asdf 109 RenameRemote("asdf") 110 111 assert.Equal(t, []string{"asdf"}, configFile.GetSectionList()) 112 assert.Equal(t, "config_test_remote", FileGet("asdf", "type")) 113 assert.Equal(t, "true", FileGet("asdf", "bool")) 114 assert.Equal(t, "secret", obscure.MustReveal(FileGet("asdf", "pass"))) 115 116 // delete remote 117 DeleteRemote("asdf") 118 assert.Equal(t, []string{}, configFile.GetSectionList()) 119 } 120 121 func TestCreateUpatePasswordRemote(t *testing.T) { 122 defer testConfigFile(t, "update.conf")() 123 124 require.NoError(t, CreateRemote("test2", "config_test_remote", rc.Params{ 125 "bool": true, 126 "pass": "potato", 127 })) 128 129 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 130 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 131 assert.Equal(t, "true", FileGet("test2", "bool")) 132 assert.Equal(t, "potato", obscure.MustReveal(FileGet("test2", "pass"))) 133 134 require.NoError(t, UpdateRemote("test2", rc.Params{ 135 "bool": false, 136 "pass": obscure.MustObscure("potato2"), 137 "spare": "spare", 138 })) 139 140 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 141 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 142 assert.Equal(t, "false", FileGet("test2", "bool")) 143 assert.Equal(t, "potato2", obscure.MustReveal(FileGet("test2", "pass"))) 144 145 require.NoError(t, PasswordRemote("test2", rc.Params{ 146 "pass": "potato3", 147 })) 148 149 assert.Equal(t, []string{"test2"}, configFile.GetSectionList()) 150 assert.Equal(t, "config_test_remote", FileGet("test2", "type")) 151 assert.Equal(t, "false", FileGet("test2", "bool")) 152 assert.Equal(t, "potato3", obscure.MustReveal(FileGet("test2", "pass"))) 153 } 154 155 // Test some error cases 156 func TestReveal(t *testing.T) { 157 for _, test := range []struct { 158 in string 159 wantErr string 160 }{ 161 {"YmJiYmJiYmJiYmJiYmJiYp*gcEWbAw", "base64 decode failed when revealing password - is it obscured?: illegal base64 data at input byte 22"}, 162 {"aGVsbG8", "input too short when revealing password - is it obscured?"}, 163 {"", "input too short when revealing password - is it obscured?"}, 164 } { 165 gotString, gotErr := obscure.Reveal(test.in) 166 assert.Equal(t, "", gotString) 167 assert.Equal(t, test.wantErr, gotErr.Error()) 168 } 169 } 170 171 func TestConfigLoad(t *testing.T) { 172 oldConfigPath := ConfigPath 173 ConfigPath = "./testdata/plain.conf" 174 defer func() { 175 ConfigPath = oldConfigPath 176 }() 177 configKey = nil // reset password 178 c, err := loadConfigFile() 179 if err != nil { 180 t.Fatal(err) 181 } 182 sections := c.GetSectionList() 183 var expect = []string{"RCLONE_ENCRYPT_V0", "nounc", "unc"} 184 assert.Equal(t, expect, sections) 185 186 keys := c.GetKeyList("nounc") 187 expect = []string{"type", "nounc"} 188 assert.Equal(t, expect, keys) 189 } 190 191 func TestConfigLoadEncrypted(t *testing.T) { 192 var err error 193 oldConfigPath := ConfigPath 194 ConfigPath = "./testdata/encrypted.conf" 195 defer func() { 196 ConfigPath = oldConfigPath 197 configKey = nil // reset password 198 }() 199 200 // Set correct password 201 err = setConfigPassword("asdf") 202 require.NoError(t, err) 203 c, err := loadConfigFile() 204 require.NoError(t, err) 205 sections := c.GetSectionList() 206 var expect = []string{"nounc", "unc"} 207 assert.Equal(t, expect, sections) 208 209 keys := c.GetKeyList("nounc") 210 expect = []string{"type", "nounc"} 211 assert.Equal(t, expect, keys) 212 } 213 214 func TestConfigLoadEncryptedFailures(t *testing.T) { 215 var err error 216 217 // This file should be too short to be decoded. 218 oldConfigPath := ConfigPath 219 ConfigPath = "./testdata/enc-short.conf" 220 defer func() { ConfigPath = oldConfigPath }() 221 _, err = loadConfigFile() 222 require.Error(t, err) 223 224 // This file contains invalid base64 characters. 225 ConfigPath = "./testdata/enc-invalid.conf" 226 _, err = loadConfigFile() 227 require.Error(t, err) 228 229 // This file contains invalid base64 characters. 230 ConfigPath = "./testdata/enc-too-new.conf" 231 _, err = loadConfigFile() 232 require.Error(t, err) 233 234 // This file does not exist. 235 ConfigPath = "./testdata/filenotfound.conf" 236 c, err := loadConfigFile() 237 assert.Equal(t, errorConfigFileNotFound, err) 238 assert.Nil(t, c) 239 } 240 241 func TestPassword(t *testing.T) { 242 defer func() { 243 configKey = nil // reset password 244 }() 245 var err error 246 // Empty password should give error 247 err = setConfigPassword(" \t ") 248 require.Error(t, err) 249 250 // Test invalid utf8 sequence 251 err = setConfigPassword(string([]byte{0xff, 0xfe, 0xfd}) + "abc") 252 require.Error(t, err) 253 254 // Simple check of wrong passwords 255 hashedKeyCompare(t, "mis", "match", false) 256 257 // Check that passwords match after unicode normalization 258 hashedKeyCompare(t, "ff\u0041\u030A", "ffÅ", true) 259 260 // Check that passwords preserves case 261 hashedKeyCompare(t, "abcdef", "ABCDEF", false) 262 263 } 264 265 func hashedKeyCompare(t *testing.T, a, b string, shouldMatch bool) { 266 err := setConfigPassword(a) 267 require.NoError(t, err) 268 k1 := configKey 269 270 err = setConfigPassword(b) 271 require.NoError(t, err) 272 k2 := configKey 273 274 if shouldMatch { 275 assert.Equal(t, k1, k2) 276 } else { 277 assert.NotEqual(t, k1, k2) 278 } 279 } 280 281 func TestMatchProvider(t *testing.T) { 282 for _, test := range []struct { 283 config string 284 provider string 285 want bool 286 }{ 287 {"", "", true}, 288 {"one", "one", true}, 289 {"one,two", "two", true}, 290 {"one,two,three", "two", true}, 291 {"one", "on", false}, 292 {"one,two,three", "tw", false}, 293 {"!one,two,three", "two", false}, 294 {"!one,two,three", "four", true}, 295 } { 296 what := fmt.Sprintf("%q,%q", test.config, test.provider) 297 got := matchProvider(test.config, test.provider) 298 assert.Equal(t, test.want, got, what) 299 } 300 } 301 302 func TestFileRefresh(t *testing.T) { 303 defer testConfigFile(t, "refresh.conf")() 304 require.NoError(t, CreateRemote("refresh_test", "config_test_remote", rc.Params{ 305 "bool": true, 306 })) 307 b, err := ioutil.ReadFile(ConfigPath) 308 assert.NoError(t, err) 309 310 b = bytes.Replace(b, []byte("refresh_test"), []byte("refreshed_test"), 1) 311 err = ioutil.WriteFile(ConfigPath, b, 0644) 312 assert.NoError(t, err) 313 314 assert.NotEqual(t, []string{"refreshed_test"}, configFile.GetSectionList()) 315 err = FileRefresh() 316 assert.NoError(t, err) 317 assert.Equal(t, []string{"refreshed_test"}, configFile.GetSectionList()) 318 }