Implement password Option and re-implement editing
Editing now shows all the options for the fs and asks one at a time whether they should be changed.
This commit is contained in:
parent
6a4e424630
commit
b1de4c8cba
3 changed files with 93 additions and 58 deletions
132
fs/config.go
132
fs/config.go
|
@ -493,7 +493,7 @@ func loadConfigFile() (*goconfig.ConfigFile, error) {
|
||||||
var out []byte
|
var out []byte
|
||||||
for {
|
for {
|
||||||
if len(configKey) == 0 && envpw != "" {
|
if len(configKey) == 0 && envpw != "" {
|
||||||
err := setPassword(envpw)
|
err := setConfigPassword(envpw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Using RCLONE_CONFIG_PASS returned:", err)
|
fmt.Println("Using RCLONE_CONFIG_PASS returned:", err)
|
||||||
envpw = ""
|
envpw = ""
|
||||||
|
@ -505,7 +505,7 @@ func loadConfigFile() (*goconfig.ConfigFile, error) {
|
||||||
if !*AskPassword {
|
if !*AskPassword {
|
||||||
return nil, errors.New("unable to decrypt configuration and not allowed to ask for password - set RCLONE_CONFIG_PASS to your configuration password")
|
return nil, errors.New("unable to decrypt configuration and not allowed to ask for password - set RCLONE_CONFIG_PASS to your configuration password")
|
||||||
}
|
}
|
||||||
getPassword("Enter configuration password:")
|
getConfigPassword("Enter configuration password:")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nonce is first 24 bytes of the ciphertext
|
// Nonce is first 24 bytes of the ciphertext
|
||||||
|
@ -529,16 +529,57 @@ func loadConfigFile() (*goconfig.ConfigFile, error) {
|
||||||
return goconfig.LoadFromReader(bytes.NewBuffer(out))
|
return goconfig.LoadFromReader(bytes.NewBuffer(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPassword will query the user for a password the
|
// checkPassword normalises and validates the password
|
||||||
|
func checkPassword(password string) (string, error) {
|
||||||
|
if !utf8.ValidString(password) {
|
||||||
|
return "", errors.New("password contains invalid utf8 characters")
|
||||||
|
}
|
||||||
|
// Remove leading+trailing whitespace
|
||||||
|
password = strings.TrimSpace(password)
|
||||||
|
// Normalize to reduce weird variations.
|
||||||
|
password = norm.NFKC.String(password)
|
||||||
|
if len(password) == 0 {
|
||||||
|
return "", errors.New("no characters in password")
|
||||||
|
}
|
||||||
|
return password, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPassword asks the user for a password with the prompt given.
|
||||||
|
func GetPassword(prompt string) string {
|
||||||
|
fmt.Println(prompt)
|
||||||
|
for {
|
||||||
|
fmt.Print("password:")
|
||||||
|
password := ReadPassword()
|
||||||
|
password, err := checkPassword(password)
|
||||||
|
if err == nil {
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
fmt.Printf("Bad password: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangePassword will query the user twice for the named password. If
|
||||||
|
// the same password is entered it is returned.
|
||||||
|
func ChangePassword(name string) string {
|
||||||
|
for {
|
||||||
|
a := GetPassword(fmt.Sprintf("Enter %s password:", name))
|
||||||
|
b := GetPassword(fmt.Sprintf("Confirm %s password:", name))
|
||||||
|
if a == b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
fmt.Println("Passwords do not match!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConfigPassword will query the user for a password the
|
||||||
// first time it is required.
|
// first time it is required.
|
||||||
func getPassword(q string) {
|
func getConfigPassword(q string) {
|
||||||
if len(configKey) != 0 {
|
if len(configKey) != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
fmt.Println(q)
|
password := GetPassword(q)
|
||||||
fmt.Print("password:")
|
err := setConfigPassword(password)
|
||||||
err := setPassword(ReadPassword())
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -546,24 +587,17 @@ func getPassword(q string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setPassword will set the configKey to the hash of
|
// setConfigPassword will set the configKey to the hash of
|
||||||
// the password. If the length of the password is
|
// the password. If the length of the password is
|
||||||
// zero after trimming+normalization, an error is returned.
|
// zero after trimming+normalization, an error is returned.
|
||||||
func setPassword(password string) error {
|
func setConfigPassword(password string) error {
|
||||||
if !utf8.ValidString(password) {
|
password, err := checkPassword(password)
|
||||||
return errors.New("password contains invalid utf8 characters")
|
if err != nil {
|
||||||
}
|
return err
|
||||||
// Remove leading+trailing whitespace
|
|
||||||
password = strings.TrimSpace(password)
|
|
||||||
|
|
||||||
// Normalize to reduce weird variations.
|
|
||||||
password = norm.NFKC.String(password)
|
|
||||||
if len(password) == 0 {
|
|
||||||
return errors.New("no characters in password")
|
|
||||||
}
|
}
|
||||||
// Create SHA256 has of the password
|
// Create SHA256 has of the password
|
||||||
sha := sha256.New()
|
sha := sha256.New()
|
||||||
_, err := sha.Write([]byte("[" + password + "][rclone-config]"))
|
_, err = sha.Write([]byte("[" + password + "][rclone-config]"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -571,6 +605,17 @@ func setPassword(password string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// changeConfigPassword will query the user twice
|
||||||
|
// for a password. If the same password is entered
|
||||||
|
// twice the key is updated.
|
||||||
|
func changeConfigPassword() {
|
||||||
|
err := setConfigPassword(ChangePassword("NEW configuration"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to set config password: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SaveConfig saves configuration file.
|
// SaveConfig saves configuration file.
|
||||||
// if configKey has been set, the file will be encrypted.
|
// if configKey has been set, the file will be encrypted.
|
||||||
func SaveConfig() {
|
func SaveConfig() {
|
||||||
|
@ -808,6 +853,9 @@ func RemoteConfig(name string) {
|
||||||
// ChooseOption asks the user to choose an option
|
// ChooseOption asks the user to choose an option
|
||||||
func ChooseOption(o *Option) string {
|
func ChooseOption(o *Option) string {
|
||||||
fmt.Println(o.Help)
|
fmt.Println(o.Help)
|
||||||
|
if o.IsPassword {
|
||||||
|
return MustObscure(ChangePassword("the"))
|
||||||
|
}
|
||||||
if len(o.Examples) > 0 {
|
if len(o.Examples) > 0 {
|
||||||
var values []string
|
var values []string
|
||||||
var help []string
|
var help []string
|
||||||
|
@ -854,20 +902,21 @@ func NewRemote(name string) {
|
||||||
SaveConfig()
|
SaveConfig()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
EditRemote(name)
|
EditRemote(fs, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditRemote gets the user to edit a remote
|
// EditRemote gets the user to edit a remote
|
||||||
func EditRemote(name string) {
|
func EditRemote(fs *RegInfo, name string) {
|
||||||
ShowRemote(name)
|
ShowRemote(name)
|
||||||
fmt.Printf("Edit remote\n")
|
fmt.Printf("Edit remote\n")
|
||||||
for {
|
for {
|
||||||
for _, key := range ConfigFile.GetKeyList(name) {
|
for _, option := range fs.Options {
|
||||||
|
key := option.Name
|
||||||
value := ConfigFile.MustValue(name, key)
|
value := ConfigFile.MustValue(name, key)
|
||||||
fmt.Printf("Press enter to accept current value, or type in a new one\n")
|
fmt.Printf("Value %q = %q\n", key, value)
|
||||||
fmt.Printf("%s = %s>", key, value)
|
fmt.Printf("Edit? (y/n)>\n")
|
||||||
newValue := ReadLine()
|
if Confirm() {
|
||||||
if newValue != "" {
|
newValue := ChooseOption(&option)
|
||||||
ConfigFile.SetValue(name, key, newValue)
|
ConfigFile.SetValue(name, key, newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -901,7 +950,11 @@ func EditConfig() {
|
||||||
switch i := Command(what); i {
|
switch i := Command(what); i {
|
||||||
case 'e':
|
case 'e':
|
||||||
name := ChooseRemote()
|
name := ChooseRemote()
|
||||||
EditRemote(name)
|
fs, err := Find(ConfigFile.MustValue(name, "type"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to find fs: %v", err)
|
||||||
|
}
|
||||||
|
EditRemote(fs, name)
|
||||||
case 'n':
|
case 'n':
|
||||||
nameLoop:
|
nameLoop:
|
||||||
for {
|
for {
|
||||||
|
@ -941,7 +994,7 @@ func SetPassword() {
|
||||||
what := []string{"cChange Password", "uUnencrypt configuration", "qQuit to main menu"}
|
what := []string{"cChange Password", "uUnencrypt configuration", "qQuit to main menu"}
|
||||||
switch i := Command(what); i {
|
switch i := Command(what); i {
|
||||||
case 'c':
|
case 'c':
|
||||||
changePassword()
|
changeConfigPassword()
|
||||||
SaveConfig()
|
SaveConfig()
|
||||||
fmt.Println("Password changed")
|
fmt.Println("Password changed")
|
||||||
continue
|
continue
|
||||||
|
@ -959,7 +1012,7 @@ func SetPassword() {
|
||||||
what := []string{"aAdd Password", "qQuit to main menu"}
|
what := []string{"aAdd Password", "qQuit to main menu"}
|
||||||
switch i := Command(what); i {
|
switch i := Command(what); i {
|
||||||
case 'a':
|
case 'a':
|
||||||
changePassword()
|
changeConfigPassword()
|
||||||
SaveConfig()
|
SaveConfig()
|
||||||
fmt.Println("Password set")
|
fmt.Println("Password set")
|
||||||
continue
|
continue
|
||||||
|
@ -970,25 +1023,6 @@ func SetPassword() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// changePassword will query the user twice
|
|
||||||
// for a password. If the same password is entered
|
|
||||||
// twice the key is updated.
|
|
||||||
func changePassword() {
|
|
||||||
for {
|
|
||||||
configKey = nil
|
|
||||||
getPassword("Enter NEW configuration password:")
|
|
||||||
a := configKey
|
|
||||||
// re-enter password
|
|
||||||
configKey = nil
|
|
||||||
getPassword("Confirm NEW password:")
|
|
||||||
b := configKey
|
|
||||||
if bytes.Equal(a, b) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("Passwords does not match!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorize is for remote authorization of headless machines.
|
// Authorize is for remote authorization of headless machines.
|
||||||
//
|
//
|
||||||
// It expects 1 or 3 arguments
|
// It expects 1 or 3 arguments
|
||||||
|
|
|
@ -162,7 +162,7 @@ func TestConfigLoadEncrypted(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Set correct password
|
// Set correct password
|
||||||
err = setPassword("asdf")
|
err = setConfigPassword("asdf")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
c, err := loadConfigFile()
|
c, err := loadConfigFile()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -208,11 +208,11 @@ func TestPassword(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
var err error
|
var err error
|
||||||
// Empty password should give error
|
// Empty password should give error
|
||||||
err = setPassword(" \t ")
|
err = setConfigPassword(" \t ")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Test invalid utf8 sequence
|
// Test invalid utf8 sequence
|
||||||
err = setPassword(string([]byte{0xff, 0xfe, 0xfd}) + "abc")
|
err = setConfigPassword(string([]byte{0xff, 0xfe, 0xfd}) + "abc")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Simple check of wrong passwords
|
// Simple check of wrong passwords
|
||||||
|
@ -230,11 +230,11 @@ func TestPassword(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashedKeyCompare(t *testing.T, a, b string, shouldMatch bool) {
|
func hashedKeyCompare(t *testing.T, a, b string, shouldMatch bool) {
|
||||||
err := setPassword(a)
|
err := setConfigPassword(a)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
k1 := configKey
|
k1 := configKey
|
||||||
|
|
||||||
err = setPassword(b)
|
err = setConfigPassword(b)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
k2 := configKey
|
k2 := configKey
|
||||||
|
|
||||||
|
|
9
fs/fs.go
9
fs/fs.go
|
@ -66,10 +66,11 @@ type RegInfo struct {
|
||||||
|
|
||||||
// Option is describes an option for the config wizard
|
// Option is describes an option for the config wizard
|
||||||
type Option struct {
|
type Option struct {
|
||||||
Name string
|
Name string
|
||||||
Help string
|
Help string
|
||||||
Optional bool
|
Optional bool
|
||||||
Examples OptionExamples
|
IsPassword bool
|
||||||
|
Examples OptionExamples
|
||||||
}
|
}
|
||||||
|
|
||||||
// OptionExamples is a slice of examples
|
// OptionExamples is a slice of examples
|
||||||
|
|
Loading…
Reference in a new issue