mirror of
https://github.com/rclone/rclone.git
synced 2025-01-10 16:28:30 +01:00
Add easier headless configuration.
This will allow setting up a remote with copy&paste of values to a headless machine. It will allow copy+pasting a token into the configuration. This requires rclone to be on a machine with a proper browser. Custom client id and secrets are supported. To test token generation, use `rclone auth "fs type"`.
This commit is contained in:
parent
c245183101
commit
bcbd30bb8a
@ -63,7 +63,7 @@ func init() {
|
|||||||
Name: "amazon cloud drive",
|
Name: "amazon cloud drive",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, acdConfig)
|
err := oauthutil.Config("amazon cloud drive", name, acdConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func init() {
|
|||||||
Name: "drive",
|
Name: "drive",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, driveConfig)
|
err := oauthutil.Config("drive", name, driveConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
44
fs/config.go
44
fs/config.go
@ -532,3 +532,47 @@ func EditConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicated from oauthutil to avoid circular reference.
|
||||||
|
const (
|
||||||
|
// ConfigClientID is the config key used to store the client id
|
||||||
|
ConfigClientID = "client_id"
|
||||||
|
|
||||||
|
// ConfigClientSecret is the config key used to store the client secret
|
||||||
|
ConfigClientSecret = "client_secret"
|
||||||
|
|
||||||
|
// ConfigAutomatic indicates that we want non-interactive configuration
|
||||||
|
ConfigAutomatic = "config_automatic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Authorize is for remote authorization of headless machines.
|
||||||
|
func Authorize() {
|
||||||
|
args := pflag.Args()[1:]
|
||||||
|
switch len(args) {
|
||||||
|
case 1, 3:
|
||||||
|
default:
|
||||||
|
log.Fatalf("Invalid number of arguments: %d", len(args))
|
||||||
|
}
|
||||||
|
newType := args[0]
|
||||||
|
fs, err := Find(newType)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to find fs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Config == nil {
|
||||||
|
log.Fatalf("No configuration on fs %v", newType)
|
||||||
|
}
|
||||||
|
// Name used for temporary fs
|
||||||
|
name := "**temp-fs**"
|
||||||
|
|
||||||
|
// Make sure we delete it
|
||||||
|
defer DeleteRemote(name)
|
||||||
|
|
||||||
|
// Indicate that we want fully automatic configuration.
|
||||||
|
ConfigFile.SetValue(name, ConfigAutomatic, "yes")
|
||||||
|
if len(args) == 3 {
|
||||||
|
ConfigFile.SetValue(name, ConfigClientID, args[1])
|
||||||
|
ConfigFile.SetValue(name, ConfigClientSecret, args[2])
|
||||||
|
}
|
||||||
|
fs.Config(name)
|
||||||
|
}
|
||||||
|
@ -59,7 +59,7 @@ func init() {
|
|||||||
Name: "google cloud storage",
|
Name: "google cloud storage",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, storageConfig)
|
err := oauthutil.Config("google cloud storage", name, storageConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func init() {
|
|||||||
Name: "hubic",
|
Name: "hubic",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, oauthConfig)
|
err := oauthutil.Config("hubic", name, oauthConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
@ -25,6 +26,9 @@ const (
|
|||||||
// ConfigClientSecret is the config key used to store the client secret
|
// ConfigClientSecret is the config key used to store the client secret
|
||||||
ConfigClientSecret = "client_secret"
|
ConfigClientSecret = "client_secret"
|
||||||
|
|
||||||
|
// ConfigAutomatic indicates that we want non-interactive configuration
|
||||||
|
ConfigAutomatic = "config_automatic"
|
||||||
|
|
||||||
// TitleBarRedirectURL is the OAuth2 redirect URL to use when the authorization
|
// TitleBarRedirectURL is the OAuth2 redirect URL to use when the authorization
|
||||||
// code should be returned in the title bar of the browser, with the page text
|
// code should be returned in the title bar of the browser, with the page text
|
||||||
// prompting the user to copy the code and paste it in the application.
|
// prompting the user to copy the code and paste it in the application.
|
||||||
@ -147,16 +151,21 @@ func Context() context.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// overrideCredentials sets the ClientID and ClientSecret from the
|
// overrideCredentials sets the ClientID and ClientSecret from the
|
||||||
// config file if they are not blank
|
// config file if they are not blank.
|
||||||
func overrideCredentials(name string, config *oauth2.Config) {
|
// If any value is overridden, true is returned.
|
||||||
|
func overrideCredentials(name string, config *oauth2.Config) bool {
|
||||||
|
changed := false
|
||||||
ClientID := fs.ConfigFile.MustValue(name, ConfigClientID)
|
ClientID := fs.ConfigFile.MustValue(name, ConfigClientID)
|
||||||
if ClientID != "" {
|
if ClientID != "" {
|
||||||
config.ClientID = ClientID
|
config.ClientID = ClientID
|
||||||
|
changed = true
|
||||||
}
|
}
|
||||||
ClientSecret := fs.ConfigFile.MustValue(name, ConfigClientSecret)
|
ClientSecret := fs.ConfigFile.MustValue(name, ConfigClientSecret)
|
||||||
if ClientSecret != "" {
|
if ClientSecret != "" {
|
||||||
config.ClientSecret = ClientSecret
|
config.ClientSecret = ClientSecret
|
||||||
|
changed = true
|
||||||
}
|
}
|
||||||
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient gets a token from the config file and configures a Client
|
// NewClient gets a token from the config file and configures a Client
|
||||||
@ -185,8 +194,10 @@ func NewClient(name string, config *oauth2.Config) (*http.Client, error) {
|
|||||||
// Config does the initial creation of the token
|
// Config does the initial creation of the token
|
||||||
//
|
//
|
||||||
// It may run an internal webserver to receive the results
|
// It may run an internal webserver to receive the results
|
||||||
func Config(name string, config *oauth2.Config) error {
|
func Config(id, name string, config *oauth2.Config) error {
|
||||||
overrideCredentials(name, config)
|
changed := overrideCredentials(name, config)
|
||||||
|
automatic := fs.ConfigFile.MustValue(name, ConfigAutomatic) != ""
|
||||||
|
|
||||||
// See if already have a token
|
// See if already have a token
|
||||||
tokenString := fs.ConfigFile.MustValue(name, "token")
|
tokenString := fs.ConfigFile.MustValue(name, "token")
|
||||||
if tokenString != "" {
|
if tokenString != "" {
|
||||||
@ -201,11 +212,42 @@ func Config(name string, config *oauth2.Config) error {
|
|||||||
switch config.RedirectURL {
|
switch config.RedirectURL {
|
||||||
case RedirectURL, RedirectPublicURL, RedirectLocalhostURL:
|
case RedirectURL, RedirectPublicURL, RedirectLocalhostURL:
|
||||||
useWebServer = true
|
useWebServer = true
|
||||||
case TitleBarRedirectURL:
|
if automatic {
|
||||||
|
break
|
||||||
|
}
|
||||||
fmt.Printf("Use auto config?\n")
|
fmt.Printf("Use auto config?\n")
|
||||||
fmt.Printf(" * Say Y if not sure\n")
|
fmt.Printf(" * Say Y if not sure\n")
|
||||||
fmt.Printf(" * Say N if you are working on a remote or headless machine or Y didn't work\n")
|
fmt.Printf(" * Say N if you are working on a remote or headless machine\n")
|
||||||
useWebServer = fs.Confirm()
|
auto := fs.Confirm()
|
||||||
|
if !auto {
|
||||||
|
fmt.Printf("For this to work, you will need rclone available on a machine that has a web browser available.\n")
|
||||||
|
fmt.Printf("Execute the following on your machine:\n")
|
||||||
|
if changed {
|
||||||
|
fmt.Printf("\trclone authorize %q %q %q\n", id, config.ClientID, config.ClientSecret)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\trclone authorize %q\n", id)
|
||||||
|
}
|
||||||
|
fmt.Println("Then paste the result below:")
|
||||||
|
code := ""
|
||||||
|
for code == "" {
|
||||||
|
fmt.Printf("result> ")
|
||||||
|
code = strings.TrimSpace(fs.ReadLine())
|
||||||
|
}
|
||||||
|
token := &oauth2.Token{}
|
||||||
|
err := json.Unmarshal([]byte(code), token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return putToken(name, token)
|
||||||
|
}
|
||||||
|
case TitleBarRedirectURL:
|
||||||
|
useWebServer = automatic
|
||||||
|
if !automatic {
|
||||||
|
fmt.Printf("Use auto config?\n")
|
||||||
|
fmt.Printf(" * Say Y if not sure\n")
|
||||||
|
fmt.Printf(" * Say N if you are working on a remote or headless machine or Y didn't work\n")
|
||||||
|
useWebServer = fs.Confirm()
|
||||||
|
}
|
||||||
if useWebServer {
|
if useWebServer {
|
||||||
// copy the config and set to use the internal webserver
|
// copy the config and set to use the internal webserver
|
||||||
configCopy := *config
|
configCopy := *config
|
||||||
@ -260,6 +302,15 @@ func Config(name string, config *oauth2.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to get token: %v", err)
|
return fmt.Errorf("Failed to get token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print code if we do automatic retrieval
|
||||||
|
if automatic {
|
||||||
|
result, err := json.Marshal(token)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to marshal token: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Paste the following into your remote machine --->\n%s\n<---End paste", result)
|
||||||
|
}
|
||||||
return putToken(name, token)
|
return putToken(name, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ func init() {
|
|||||||
Name: "onedrive",
|
Name: "onedrive",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, oauthConfig)
|
err := oauthutil.Config("onedrive", name, oauthConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
12
rclone.go
12
rclone.go
@ -224,6 +224,18 @@ var Commands = []Command{
|
|||||||
},
|
},
|
||||||
NoStats: true,
|
NoStats: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "authorize",
|
||||||
|
Help: `
|
||||||
|
Remote authorization.`,
|
||||||
|
Run: func(fdst, fsrc fs.Fs) error {
|
||||||
|
fs.Authorize()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
NoStats: true,
|
||||||
|
MinArgs: 1,
|
||||||
|
MaxArgs: 3,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "help",
|
Name: "help",
|
||||||
Help: `
|
Help: `
|
||||||
|
@ -45,7 +45,7 @@ func init() {
|
|||||||
Name: "yandex",
|
Name: "yandex",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config(name, oauthConfig)
|
err := oauthutil.Config("yandex", name, oauthConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user