From 8bf265c775ac26f750f2a3c1a843c30136aefb35 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 22 May 2020 10:59:45 +0100 Subject: [PATCH] box: allow authentication with access token - fixes #4114 --- backend/box/box.go | 64 ++++++++++++++++++++++++++++++--------------- docs/content/box.md | 13 +++++++-- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/backend/box/box.go b/backend/box/box.go index 73b75fa3c..25b8d3ba8 100644 --- a/backend/box/box.go +++ b/backend/box/box.go @@ -87,13 +87,16 @@ func init() { Config: func(name string, m configmap.Mapper) { jsonFile, ok := m.Get("box_config_file") boxSubType, boxSubTypeOk := m.Get("box_sub_type") + boxAccessToken, boxAccessTokenOk := m.Get("access_token") var err error + // If using box config.json, use JWT auth if ok && boxSubTypeOk && jsonFile != "" && boxSubType != "" { err = refreshJWTToken(jsonFile, boxSubType, name, m) if err != nil { log.Fatalf("Failed to configure token with jwt authentication: %v", err) } - } else { + // Else, if not using an access token, use oauth2 + } else if boxAccessToken == "" || !boxAccessTokenOk { err = oauthutil.Config("box", name, m, oauthConfig, nil) if err != nil { log.Fatalf("Failed to configure token with oauth authentication: %v", err) @@ -114,6 +117,9 @@ func init() { }, { Name: "box_config_file", Help: "Box App config.json location\nLeave blank normally." + env.ShellExpandHelp, + }, { + Name: "access_token", + Help: "Box App Primary Access Token\nLeave blank normally.", }, { Name: "box_sub_type", Default: "user", @@ -247,6 +253,7 @@ type Options struct { CommitRetries int `config:"commit_retries"` Enc encoder.MultiEncoder `config:"encoding"` RootFolderID string `config:"root_folder_id"` + AccessToken string `config:"access_token"` } // Fs represents a remote box @@ -385,16 +392,22 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { } root = parsePath(root) - oAuthClient, ts, err := oauthutil.NewClient(name, m, oauthConfig) - if err != nil { - return nil, errors.Wrap(err, "failed to configure Box") + + client := fshttp.NewClient(fs.Config) + var ts *oauthutil.TokenSource + // If not using an accessToken, create an oauth client and tokensource + if opt.AccessToken == "" { + client, ts, err = oauthutil.NewClient(name, m, oauthConfig) + if err != nil { + return nil, errors.Wrap(err, "failed to configure Box") + } } f := &Fs{ name: name, root: root, opt: *opt, - srv: rest.NewClient(oAuthClient).SetRoot(rootURL), + srv: rest.NewClient(client).SetRoot(rootURL), pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))), uploadToken: pacer.NewTokenDispenser(fs.Config.Transfers), } @@ -404,23 +417,30 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { }).Fill(f) f.srv.SetErrorHandler(errorHandler) + // If using an accessToken, set the Authorization header + if f.opt.AccessToken != "" { + f.srv.SetHeader("Authorization", "Bearer "+f.opt.AccessToken) + } + jsonFile, ok := m.Get("box_config_file") boxSubType, boxSubTypeOk := m.Get("box_sub_type") - // If using box config.json and JWT, renewing should just refresh the token and - // should do so whether there are uploads pending or not. - if ok && boxSubTypeOk && jsonFile != "" && boxSubType != "" { - f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { - err := refreshJWTToken(jsonFile, boxSubType, name, m) - return err - }) - f.tokenRenewer.Start() - } else { - // Renew the token in the background - f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { - _, err := f.readMetaDataForPath(ctx, "") - return err - }) + if ts != nil { + // If using box config.json and JWT, renewing should just refresh the token and + // should do so whether there are uploads pending or not. + if ok && boxSubTypeOk && jsonFile != "" && boxSubType != "" { + f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { + err := refreshJWTToken(jsonFile, boxSubType, name, m) + return err + }) + f.tokenRenewer.Start() + } else { + // Renew the token in the background + f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { + _, err := f.readMetaDataForPath(ctx, "") + return err + }) + } } // Get rootFolderID @@ -1258,8 +1278,10 @@ func (o *Object) upload(ctx context.Context, in io.Reader, leaf, directoryID str // // The new object may have been created if an error is returned func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (err error) { - o.fs.tokenRenewer.Start() - defer o.fs.tokenRenewer.Stop() + if o.fs.tokenRenewer != nil { + o.fs.tokenRenewer.Start() + defer o.fs.tokenRenewer.Stop() + } size := src.Size() modTime := src.ModTime(ctx) diff --git a/docs/content/box.md b/docs/content/box.md index 9cccece03..696a80919 100644 --- a/docs/content/box.md +++ b/docs/content/box.md @@ -41,9 +41,18 @@ client_secret> Box App config.json location Leave blank normally. Enter a string value. Press Enter for the default (""). -config_json> -'enterprise' or 'user' depending on the type of token being requested. +box_config_file> +Box App Primary Access Token +Leave blank normally. +Enter a string value. Press Enter for the default (""). +access_token> + Enter a string value. Press Enter for the default ("user"). +Choose a number from below, or type in your own value + 1 / Rclone should act on behalf of a user + \ "user" + 2 / Rclone should act on behalf of a service account + \ "enterprise" box_sub_type> Remote config Use auto config?