903ede52cd
Before this change when using "rclone config create" it wasn't possible to add passwords in one go, it was necessary to call "rclone config password" to add the passwords afterwards as "rclone config create" didn't obscure passwords. After this change "rclone config create" and "rclone config update" will obscure passwords as necessary as will the corresponding API calls config/create and config/update. This makes "rclone config password" and its API config/password obsolete, however they will be left for backwards compatibility.
294 lines
7.6 KiB
Go
294 lines
7.6 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/ncw/rclone/fs"
|
|
"github.com/ncw/rclone/fs/config/obscure"
|
|
"github.com/ncw/rclone/fs/rc"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func testConfigFile(t *testing.T, configFileName string) func() {
|
|
configKey = nil // reset password
|
|
// create temp config file
|
|
tempFile, err := ioutil.TempFile("", configFileName)
|
|
assert.NoError(t, err)
|
|
path := tempFile.Name()
|
|
assert.NoError(t, tempFile.Close())
|
|
|
|
// temporarily adapt configuration
|
|
oldOsStdout := os.Stdout
|
|
oldConfigPath := ConfigPath
|
|
oldConfig := fs.Config
|
|
oldConfigFile := configFile
|
|
oldReadLine := ReadLine
|
|
os.Stdout = nil
|
|
ConfigPath = path
|
|
fs.Config = &fs.ConfigInfo{}
|
|
configFile = nil
|
|
|
|
LoadConfig()
|
|
assert.Equal(t, []string{}, getConfigData().GetSectionList())
|
|
|
|
// Fake a remote
|
|
fs.Register(&fs.RegInfo{
|
|
Name: "config_test_remote",
|
|
Options: fs.Options{
|
|
{
|
|
Name: "bool",
|
|
Default: false,
|
|
IsPassword: false,
|
|
},
|
|
{
|
|
Name: "pass",
|
|
Default: "",
|
|
IsPassword: true,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Undo the above
|
|
return func() {
|
|
err := os.Remove(path)
|
|
assert.NoError(t, err)
|
|
|
|
os.Stdout = oldOsStdout
|
|
ConfigPath = oldConfigPath
|
|
ReadLine = oldReadLine
|
|
fs.Config = oldConfig
|
|
configFile = oldConfigFile
|
|
}
|
|
}
|
|
|
|
func TestCRUD(t *testing.T) {
|
|
defer testConfigFile(t, "crud.conf")()
|
|
|
|
// expect script for creating remote
|
|
i := 0
|
|
ReadLine = func() string {
|
|
answers := []string{
|
|
"config_test_remote", // type
|
|
"true", // bool value
|
|
"y", // type my own password
|
|
"secret", // password
|
|
"secret", // repeat
|
|
"y", // looks good, save
|
|
}
|
|
i = i + 1
|
|
return answers[i-1]
|
|
}
|
|
|
|
NewRemote("test")
|
|
|
|
assert.Equal(t, []string{"test"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("test", "type"))
|
|
assert.Equal(t, "true", FileGet("test", "bool"))
|
|
assert.Equal(t, "secret", obscure.MustReveal(FileGet("test", "pass")))
|
|
|
|
// normal rename, test → asdf
|
|
ReadLine = func() string { return "asdf" }
|
|
RenameRemote("test")
|
|
|
|
assert.Equal(t, []string{"asdf"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("asdf", "type"))
|
|
assert.Equal(t, "true", FileGet("asdf", "bool"))
|
|
assert.Equal(t, "secret", obscure.MustReveal(FileGet("asdf", "pass")))
|
|
|
|
// no-op rename, asdf → asdf
|
|
RenameRemote("asdf")
|
|
|
|
assert.Equal(t, []string{"asdf"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("asdf", "type"))
|
|
assert.Equal(t, "true", FileGet("asdf", "bool"))
|
|
assert.Equal(t, "secret", obscure.MustReveal(FileGet("asdf", "pass")))
|
|
|
|
// delete remote
|
|
DeleteRemote("asdf")
|
|
assert.Equal(t, []string{}, configFile.GetSectionList())
|
|
}
|
|
|
|
func TestCreateUpatePasswordRemote(t *testing.T) {
|
|
defer testConfigFile(t, "update.conf")()
|
|
|
|
require.NoError(t, CreateRemote("test2", "config_test_remote", rc.Params{
|
|
"bool": true,
|
|
"pass": "potato",
|
|
}))
|
|
|
|
assert.Equal(t, []string{"test2"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("test2", "type"))
|
|
assert.Equal(t, "true", FileGet("test2", "bool"))
|
|
assert.Equal(t, "potato", obscure.MustReveal(FileGet("test2", "pass")))
|
|
|
|
require.NoError(t, UpdateRemote("test2", rc.Params{
|
|
"bool": false,
|
|
"pass": obscure.MustObscure("potato2"),
|
|
"spare": "spare",
|
|
}))
|
|
|
|
assert.Equal(t, []string{"test2"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("test2", "type"))
|
|
assert.Equal(t, "false", FileGet("test2", "bool"))
|
|
assert.Equal(t, "potato2", obscure.MustReveal(FileGet("test2", "pass")))
|
|
|
|
require.NoError(t, PasswordRemote("test2", rc.Params{
|
|
"pass": "potato3",
|
|
}))
|
|
|
|
assert.Equal(t, []string{"test2"}, configFile.GetSectionList())
|
|
assert.Equal(t, "config_test_remote", FileGet("test2", "type"))
|
|
assert.Equal(t, "false", FileGet("test2", "bool"))
|
|
assert.Equal(t, "potato3", obscure.MustReveal(FileGet("test2", "pass")))
|
|
}
|
|
|
|
// Test some error cases
|
|
func TestReveal(t *testing.T) {
|
|
for _, test := range []struct {
|
|
in string
|
|
wantErr string
|
|
}{
|
|
{"YmJiYmJiYmJiYmJiYmJiYp*gcEWbAw", "base64 decode failed when revealing password - is it obscured?: illegal base64 data at input byte 22"},
|
|
{"aGVsbG8", "input too short when revealing password - is it obscured?"},
|
|
{"", "input too short when revealing password - is it obscured?"},
|
|
} {
|
|
gotString, gotErr := obscure.Reveal(test.in)
|
|
assert.Equal(t, "", gotString)
|
|
assert.Equal(t, test.wantErr, gotErr.Error())
|
|
}
|
|
}
|
|
|
|
func TestConfigLoad(t *testing.T) {
|
|
oldConfigPath := ConfigPath
|
|
ConfigPath = "./testdata/plain.conf"
|
|
defer func() {
|
|
ConfigPath = oldConfigPath
|
|
}()
|
|
configKey = nil // reset password
|
|
c, err := loadConfigFile()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sections := c.GetSectionList()
|
|
var expect = []string{"RCLONE_ENCRYPT_V0", "nounc", "unc"}
|
|
assert.Equal(t, expect, sections)
|
|
|
|
keys := c.GetKeyList("nounc")
|
|
expect = []string{"type", "nounc"}
|
|
assert.Equal(t, expect, keys)
|
|
}
|
|
|
|
func TestConfigLoadEncrypted(t *testing.T) {
|
|
var err error
|
|
oldConfigPath := ConfigPath
|
|
ConfigPath = "./testdata/encrypted.conf"
|
|
defer func() {
|
|
ConfigPath = oldConfigPath
|
|
configKey = nil // reset password
|
|
}()
|
|
|
|
// Set correct password
|
|
err = setConfigPassword("asdf")
|
|
require.NoError(t, err)
|
|
c, err := loadConfigFile()
|
|
require.NoError(t, err)
|
|
sections := c.GetSectionList()
|
|
var expect = []string{"nounc", "unc"}
|
|
assert.Equal(t, expect, sections)
|
|
|
|
keys := c.GetKeyList("nounc")
|
|
expect = []string{"type", "nounc"}
|
|
assert.Equal(t, expect, keys)
|
|
}
|
|
|
|
func TestConfigLoadEncryptedFailures(t *testing.T) {
|
|
var err error
|
|
|
|
// This file should be too short to be decoded.
|
|
oldConfigPath := ConfigPath
|
|
ConfigPath = "./testdata/enc-short.conf"
|
|
defer func() { ConfigPath = oldConfigPath }()
|
|
_, err = loadConfigFile()
|
|
require.Error(t, err)
|
|
|
|
// This file contains invalid base64 characters.
|
|
ConfigPath = "./testdata/enc-invalid.conf"
|
|
_, err = loadConfigFile()
|
|
require.Error(t, err)
|
|
|
|
// This file contains invalid base64 characters.
|
|
ConfigPath = "./testdata/enc-too-new.conf"
|
|
_, err = loadConfigFile()
|
|
require.Error(t, err)
|
|
|
|
// This file does not exist.
|
|
ConfigPath = "./testdata/filenotfound.conf"
|
|
c, err := loadConfigFile()
|
|
assert.Equal(t, errorConfigFileNotFound, err)
|
|
assert.Nil(t, c)
|
|
}
|
|
|
|
func TestPassword(t *testing.T) {
|
|
defer func() {
|
|
configKey = nil // reset password
|
|
}()
|
|
var err error
|
|
// Empty password should give error
|
|
err = setConfigPassword(" \t ")
|
|
require.Error(t, err)
|
|
|
|
// Test invalid utf8 sequence
|
|
err = setConfigPassword(string([]byte{0xff, 0xfe, 0xfd}) + "abc")
|
|
require.Error(t, err)
|
|
|
|
// Simple check of wrong passwords
|
|
hashedKeyCompare(t, "mis", "match", false)
|
|
|
|
// Check that passwords match after unicode normalization
|
|
hashedKeyCompare(t, "ff\u0041\u030A", "ffÅ", true)
|
|
|
|
// Check that passwords preserves case
|
|
hashedKeyCompare(t, "abcdef", "ABCDEF", false)
|
|
|
|
}
|
|
|
|
func hashedKeyCompare(t *testing.T, a, b string, shouldMatch bool) {
|
|
err := setConfigPassword(a)
|
|
require.NoError(t, err)
|
|
k1 := configKey
|
|
|
|
err = setConfigPassword(b)
|
|
require.NoError(t, err)
|
|
k2 := configKey
|
|
|
|
if shouldMatch {
|
|
assert.Equal(t, k1, k2)
|
|
} else {
|
|
assert.NotEqual(t, k1, k2)
|
|
}
|
|
}
|
|
|
|
func TestMatchProvider(t *testing.T) {
|
|
for _, test := range []struct {
|
|
config string
|
|
provider string
|
|
want bool
|
|
}{
|
|
{"", "", true},
|
|
{"one", "one", true},
|
|
{"one,two", "two", true},
|
|
{"one,two,three", "two", true},
|
|
{"one", "on", false},
|
|
{"one,two,three", "tw", false},
|
|
{"!one,two,three", "two", false},
|
|
{"!one,two,three", "four", true},
|
|
} {
|
|
what := fmt.Sprintf("%q,%q", test.config, test.provider)
|
|
got := matchProvider(test.config, test.provider)
|
|
assert.Equal(t, test.want, got, what)
|
|
}
|
|
}
|