From f52ae75a51a04188d728d8888112fb1fedb6c519 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Sun, 4 Apr 2021 14:56:42 +0100 Subject: [PATCH] rclone authorize: Send and receive extra config options to fix oauth Before this change any backends which required extra config in the oauth phase (like the `region` for zoho) didn't work with `rclone authorize`. This change serializes the extra config and passes it to `rclone authorize` and returns new config items to be set from rclone authorize. `rclone authorize` will still accept its previous configuration parameters for use with old rclones. Fixes #5178 --- fs/config/authorize.go | 24 ++++++++++++++++----- lib/oauthutil/oauthutil.go | 43 +++++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/fs/config/authorize.go b/fs/config/authorize.go index 404b5af5d..fc1a291bb 100644 --- a/fs/config/authorize.go +++ b/fs/config/authorize.go @@ -18,7 +18,7 @@ import ( func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error { ctx = suppressConfirm(ctx) switch len(args) { - case 1, 3: + case 1, 2, 3: default: return errors.Errorf("invalid number of arguments: %d", len(args)) } @@ -40,7 +40,13 @@ func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error { inM[ConfigAuthNoBrowser] = "true" } - if len(args) == 3 { + // Add extra parameters if supplied + if len(args) == 2 { + err := inM.Decode(args[1]) + if err != nil { + return err + } + } else if len(args) == 3 { inM[ConfigClientID] = args[1] inM[ConfigClientSecret] = args[2] } @@ -52,13 +58,21 @@ func Authorize(ctx context.Context, args []string, noAutoBrowser bool) error { outM := configmap.Simple{} m.ClearSetters() m.AddSetter(outM) + m.AddGetter(outM, configmap.PriorityNormal) ri.Config(ctx, name, m) - // Print code if we are doing a manual auth - fmt.Printf("Paste the following into your remote machine --->\n%s\n<---End paste\n", outM["token"]) + // Print the code for the user to paste + out := outM["token"] - fs.Debugf(nil, "Set parameters %q", outM) + // If received a config blob, then return one + if len(args) == 2 { + out, err = outM.Encode() + if err != nil { + return err + } + } + fmt.Printf("Paste the following into your remote machine --->\n%s\n<---End paste\n", out) return nil } diff --git a/lib/oauthutil/oauthutil.go b/lib/oauthutil/oauthutil.go index 667d1645c..d7594e56c 100644 --- a/lib/oauthutil/oauthutil.go +++ b/lib/oauthutil/oauthutil.go @@ -447,19 +447,46 @@ Execute the following on the machine with the web browser (same rclone version recommended): `) - if changed { - fmt.Printf("\trclone authorize %q -- %q %q\n", id, oauthConfig.ClientID, oauthConfig.ClientSecret) + // Find the configuration + ri, err := fs.Find(id) + if err != nil { + return errors.Wrap(err, "oauthutil authorize") + } + // Find the overridden options + inM := ri.Options.NonDefault(m) + delete(inM, config.ConfigToken) // delete token as we are refreshing it + for k, v := range inM { + fs.Debugf(nil, "sending %s = %q", k, v) + } + // Encode them into a string + mCopyString, err := inM.Encode() + if err != nil { + return errors.Wrap(err, "oauthutil authorize encode") + } + // Write what the user has to do + if len(mCopyString) > 0 { + fmt.Printf("\trclone authorize %q %q\n", id, mCopyString) } else { fmt.Printf("\trclone authorize %q\n", id) } fmt.Println("\nThen paste the result below:") - code := config.ReadNonEmptyLine("result> ") - token := &oauth2.Token{} - err := json.Unmarshal([]byte(code), token) - if err != nil { - return err + // Read the updates to the config + var outM configmap.Simple + for { + outM = configmap.Simple{} + code := config.ReadNonEmptyLine("result> ") + err = outM.Decode(code) + if err == nil { + break + } + fmt.Printf("Couldn't decode response - try again (make sure you are using a matching version of rclone on both sides: %v\n", err) } - return PutToken(name, m, token, true) + // Save the config updates + for k, v := range outM { + m.Set(k, v) + fs.Debugf(nil, "received %s = %q", k, v) + } + return nil } }