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  }