forked from TrueCloudLab/rclone
drive: add service account support. Fixes #839.
This commit is contained in:
parent
4af4bbb539
commit
835ca15ec8
2 changed files with 68 additions and 9 deletions
|
@ -65,6 +65,8 @@ Google Application Client Id - leave blank normally.
|
||||||
client_id>
|
client_id>
|
||||||
Google Application Client Secret - leave blank normally.
|
Google Application Client Secret - leave blank normally.
|
||||||
client_secret>
|
client_secret>
|
||||||
|
Service Account Credentials JSON file path - needed only if you want use SA instead of interactive login.
|
||||||
|
service_account_file>
|
||||||
Remote config
|
Remote config
|
||||||
Use auto config?
|
Use auto config?
|
||||||
* Say Y if not sure
|
* Say Y if not sure
|
||||||
|
@ -113,6 +115,25 @@ To copy a local directory to a drive directory called backup
|
||||||
|
|
||||||
rclone copy /home/source remote:backup
|
rclone copy /home/source remote:backup
|
||||||
|
|
||||||
|
### Service Account support ###
|
||||||
|
|
||||||
|
You can set up rclone with Google Drive in an unattended mode,
|
||||||
|
i.e. not tied to a specific end-user Google account. This is useful
|
||||||
|
when you want to synchronise files onto machines that don't have
|
||||||
|
actively logged-in users, for example build machines.
|
||||||
|
|
||||||
|
To create a service account and obtain its credentials, go to the
|
||||||
|
[Google Developer Console](https://console.developers.google.com) and
|
||||||
|
use the "Create Credentials" button. After creating an account, a JSON
|
||||||
|
file containing the Service Account's credentials will be downloaded
|
||||||
|
onto your machine. These credentials are what rclone will use for
|
||||||
|
authentication.
|
||||||
|
|
||||||
|
To use a Service Account instead of OAuth2 token flow, enter the path
|
||||||
|
to your Service Account credentials at the `service_account_file`
|
||||||
|
prompt and rclone won't use the browser based authentication
|
||||||
|
flow.
|
||||||
|
|
||||||
### Team drives ###
|
### Team drives ###
|
||||||
|
|
||||||
If you want to configure the remote to point to a Google Team Drive
|
If you want to configure the remote to point to a Google Team Drive
|
||||||
|
|
|
@ -10,8 +10,10 @@ package drive
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -96,9 +98,12 @@ func init() {
|
||||||
Description: "Google Drive",
|
Description: "Google Drive",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: func(name string) {
|
Config: func(name string) {
|
||||||
err := oauthutil.Config("drive", name, driveConfig)
|
var err error
|
||||||
if err != nil {
|
if fs.ConfigFileGet(name, "service_account_file") == "" {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
err = oauthutil.Config("drive", name, driveConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = configTeamDrive(name)
|
err = configTeamDrive(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -111,6 +116,9 @@ func init() {
|
||||||
}, {
|
}, {
|
||||||
Name: fs.ConfigClientSecret,
|
Name: fs.ConfigClientSecret,
|
||||||
Help: "Google Application Client Secret - leave blank normally.",
|
Help: "Google Application Client Secret - leave blank normally.",
|
||||||
|
}, {
|
||||||
|
Name: "service_account_file",
|
||||||
|
Help: "Service Account Credentials JSON file path - needed only if you want use SA instead of interactive login.",
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
fs.VarP(&driveUploadCutoff, "drive-upload-cutoff", "", "Cutoff for switching to chunked upload")
|
fs.VarP(&driveUploadCutoff, "drive-upload-cutoff", "", "Cutoff for switching to chunked upload")
|
||||||
|
@ -338,9 +346,9 @@ func configTeamDrive(name string) error {
|
||||||
if !fs.Confirm() {
|
if !fs.Confirm() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
client, _, err := oauthutil.NewClient(name, driveConfig)
|
client, err := authenticate(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "config team drive failed to make oauth client")
|
return errors.Wrap(err, "config team drive failed to authenticate")
|
||||||
}
|
}
|
||||||
svc, err := drive.New(client)
|
svc, err := drive.New(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -382,6 +390,39 @@ func newPacer() *pacer.Pacer {
|
||||||
return pacer.New().SetMinSleep(minSleep).SetPacer(pacer.GoogleDrivePacer)
|
return pacer.New().SetMinSleep(minSleep).SetPacer(pacer.GoogleDrivePacer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getServiceAccountClient(keyJsonfilePath string) (*http.Client, error) {
|
||||||
|
data, err := ioutil.ReadFile(os.ExpandEnv(keyJsonfilePath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error opening credentials file")
|
||||||
|
}
|
||||||
|
conf, err := google.JWTConfigFromJSON(data, driveConfig.Scopes...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error processing credentials")
|
||||||
|
}
|
||||||
|
ctxWithSpecialClient := oauthutil.Context(fs.Config.Client())
|
||||||
|
return oauth2.NewClient(ctxWithSpecialClient, conf.TokenSource(ctxWithSpecialClient)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticate(name string) (*http.Client, error) {
|
||||||
|
var oAuthClient *http.Client
|
||||||
|
var err error
|
||||||
|
|
||||||
|
serviceAccountPath := fs.ConfigFileGet(name, "service_account_file")
|
||||||
|
if serviceAccountPath != "" {
|
||||||
|
oAuthClient, err = getServiceAccountClient(serviceAccountPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Failed to configure drive Service Account")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oAuthClient, _, err = oauthutil.NewClient(name, driveConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Failed to configure drive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oAuthClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewFs contstructs an Fs from the path, container:path
|
// NewFs contstructs an Fs from the path, container:path
|
||||||
func NewFs(name, path string) (fs.Fs, error) {
|
func NewFs(name, path string) (fs.Fs, error) {
|
||||||
if !isPowerOfTwo(int64(chunkSize)) {
|
if !isPowerOfTwo(int64(chunkSize)) {
|
||||||
|
@ -391,10 +432,7 @@ func NewFs(name, path string) (fs.Fs, error) {
|
||||||
return nil, errors.Errorf("drive: chunk size can't be less than 256k - was %v", chunkSize)
|
return nil, errors.Errorf("drive: chunk size can't be less than 256k - was %v", chunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
oAuthClient, _, err := oauthutil.NewClient(name, driveConfig)
|
oAuthClient, _ := authenticate(name)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to configure drive: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err := parseDrivePath(path)
|
root, err := parseDrivePath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue