From 08c35ae741d1e3b6ae06c311a5b814544af25b96 Mon Sep 17 00:00:00 2001 From: Dominik Sander Date: Tue, 18 Nov 2025 17:09:06 +0100 Subject: [PATCH] box: allow to configure with config file contents Especially when using rclone via rc it is helpful to configure the box backend using the contents of the config file instead of heaving to upload the file to the server that is running rclone. --- backend/box/box.go | 49 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/backend/box/box.go b/backend/box/box.go index 56bf02c39..69274e102 100644 --- a/backend/box/box.go +++ b/backend/box/box.go @@ -87,13 +87,11 @@ func init() { Description: "Box", NewFs: NewFs, Config: func(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) { - 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(ctx, jsonFile, boxSubType, name, m) + if usesJWTAuth(m) { + err = refreshJWTToken(ctx, name, m) if err != nil { return nil, fmt.Errorf("failed to configure token with jwt authentication: %w", err) } @@ -114,6 +112,11 @@ func init() { }, { Name: "box_config_file", Help: "Box App config.json location\n\nLeave blank normally." + env.ShellExpandHelp, + }, { + Name: "config_credentials", + Help: "Box App config.json contents.\n\nLeave blank normally.", + Hide: fs.OptionHideBoth, + Sensitive: true, }, { Name: "access_token", Help: "Box App Primary Access Token\n\nLeave blank normally.", @@ -184,9 +187,17 @@ See: https://developer.box.com/guides/authentication/jwt/as-user/ }) } -func refreshJWTToken(ctx context.Context, jsonFile string, boxSubType string, name string, m configmap.Mapper) error { - jsonFile = env.ShellExpand(jsonFile) - boxConfig, err := getBoxConfig(jsonFile) +func usesJWTAuth(m configmap.Mapper) bool { + jsonFile, okFile := m.Get("box_config_file") + jsonFileCredentials, okCredentials := m.Get("config_credentials") + boxSubType, boxSubTypeOk := m.Get("box_sub_type") + return (okFile || okCredentials) && boxSubTypeOk && (jsonFile != "" || jsonFileCredentials != "") && boxSubType != "" +} + +func refreshJWTToken(ctx context.Context, name string, m configmap.Mapper) error { + boxSubType, _ := m.Get("box_sub_type") + + boxConfig, err := getBoxConfig(m) if err != nil { return fmt.Errorf("get box config: %w", err) } @@ -205,12 +216,19 @@ func refreshJWTToken(ctx context.Context, jsonFile string, boxSubType string, na return err } -func getBoxConfig(configFile string) (boxConfig *api.ConfigJSON, err error) { - file, err := os.ReadFile(configFile) - if err != nil { - return nil, fmt.Errorf("box: failed to read Box config: %w", err) +func getBoxConfig(m configmap.Mapper) (boxConfig *api.ConfigJSON, err error) { + configFileCredentials, _ := m.Get("config_credentials") + configFileBytes := []byte(configFileCredentials) + + if configFileCredentials == "" { + configFile, _ := m.Get("box_config_file") + configFileBytes, err = os.ReadFile(configFile) + if err != nil { + return nil, fmt.Errorf("box: failed to read Box config: %w", err) + } } - err = json.Unmarshal(file, &boxConfig) + + err = json.Unmarshal(configFileBytes, &boxConfig) if err != nil { return nil, fmt.Errorf("box: failed to parse Box config: %w", err) } @@ -485,15 +503,12 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e f.srv.SetHeader("as-user", f.opt.Impersonate) } - jsonFile, ok := m.Get("box_config_file") - boxSubType, boxSubTypeOk := m.Get("box_sub_type") - 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 != "" { + if usesJWTAuth(m) { f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error { - err := refreshJWTToken(ctx, jsonFile, boxSubType, name, m) + err := refreshJWTToken(ctx, name, m) return err }) f.tokenRenewer.Start()