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>
|
||||
Google Application Client Secret - leave blank normally.
|
||||
client_secret>
|
||||
Service Account Credentials JSON file path - needed only if you want use SA instead of interactive login.
|
||||
service_account_file>
|
||||
Remote config
|
||||
Use auto config?
|
||||
* 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
|
||||
|
||||
### 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 ###
|
||||
|
||||
If you want to configure the remote to point to a Google Team Drive
|
||||
|
|
|
@ -10,8 +10,10 @@ package drive
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -96,9 +98,12 @@ func init() {
|
|||
Description: "Google Drive",
|
||||
NewFs: NewFs,
|
||||
Config: func(name string) {
|
||||
err := oauthutil.Config("drive", name, driveConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to configure token: %v", err)
|
||||
var err error
|
||||
if fs.ConfigFileGet(name, "service_account_file") == "" {
|
||||
err = oauthutil.Config("drive", name, driveConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to configure token: %v", err)
|
||||
}
|
||||
}
|
||||
err = configTeamDrive(name)
|
||||
if err != nil {
|
||||
|
@ -111,6 +116,9 @@ func init() {
|
|||
}, {
|
||||
Name: fs.ConfigClientSecret,
|
||||
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")
|
||||
|
@ -338,9 +346,9 @@ func configTeamDrive(name string) error {
|
|||
if !fs.Confirm() {
|
||||
return nil
|
||||
}
|
||||
client, _, err := oauthutil.NewClient(name, driveConfig)
|
||||
client, err := authenticate(name)
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -382,6 +390,39 @@ func newPacer() *pacer.Pacer {
|
|||
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
|
||||
func NewFs(name, path string) (fs.Fs, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
oAuthClient, _, err := oauthutil.NewClient(name, driveConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to configure drive: %v", err)
|
||||
}
|
||||
oAuthClient, _ := authenticate(name)
|
||||
|
||||
root, err := parseDrivePath(path)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue