mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 08:23:47 +01:00
drive: factor common authentication code into googleauth module
In preparation for Google Cloud Storage support
This commit is contained in:
parent
8a76568ea8
commit
b83441081c
128
drive/drive.go
128
drive/drive.go
@ -16,7 +16,6 @@ package drive
|
|||||||
// * files with / in name
|
// * files with / in name
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -28,9 +27,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.google.com/p/goauth2/oauth"
|
|
||||||
"code.google.com/p/google-api-go-client/drive/v2"
|
"code.google.com/p/google-api-go-client/drive/v2"
|
||||||
"github.com/ncw/rclone/fs"
|
"github.com/ncw/rclone/fs"
|
||||||
|
"github.com/ncw/rclone/googleauth"
|
||||||
"github.com/ogier/pflag"
|
"github.com/ogier/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,14 +46,22 @@ const (
|
|||||||
var (
|
var (
|
||||||
// Flags
|
// Flags
|
||||||
driveFullList = pflag.BoolP("drive-full-list", "", true, "Use a full listing for directory list. More data but usually quicker.")
|
driveFullList = pflag.BoolP("drive-full-list", "", true, "Use a full listing for directory list. More data but usually quicker.")
|
||||||
|
// Description of how to auth for this app
|
||||||
|
driveAuth = &googleauth.Auth{
|
||||||
|
Scope: "https://www.googleapis.com/auth/drive",
|
||||||
|
DefaultClientId: rcloneClientId,
|
||||||
|
DefaultClientSecret: rcloneClientSecret,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.FsInfo{
|
fs.Register(&fs.FsInfo{
|
||||||
Name: "drive",
|
Name: "drive",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Config: Config,
|
Config: func(name string) {
|
||||||
|
driveAuth.Config(name)
|
||||||
|
},
|
||||||
Options: []fs.Option{{
|
Options: []fs.Option{{
|
||||||
Name: "client_id",
|
Name: "client_id",
|
||||||
Help: "Google Application Client Id - leave blank to use rclone's.",
|
Help: "Google Application Client Id - leave blank to use rclone's.",
|
||||||
@ -65,77 +72,6 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration helper - called after the user has put in the defaults
|
|
||||||
func Config(name string) {
|
|
||||||
// See if already have a token
|
|
||||||
tokenString := fs.ConfigFile.MustValue(name, "token")
|
|
||||||
if tokenString != "" {
|
|
||||||
fmt.Printf("Already have a drive token - refresh?\n")
|
|
||||||
if !fs.Confirm() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a drive transport
|
|
||||||
t, err := newDriveTransport(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Couldn't make drive transport: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a URL for the user to visit for authorization.
|
|
||||||
authUrl := t.Config.AuthCodeURL("state")
|
|
||||||
fmt.Printf("Go to the following link in your browser\n")
|
|
||||||
fmt.Printf("%s\n", authUrl)
|
|
||||||
fmt.Printf("Log in, then type paste the token that is returned in the browser here\n")
|
|
||||||
|
|
||||||
// Read the code, and exchange it for a token.
|
|
||||||
fmt.Printf("Enter verification code> ")
|
|
||||||
authCode := fs.ReadLine()
|
|
||||||
_, err = t.Exchange(authCode)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to get token: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// A token cache to save the token in the config file section named
|
|
||||||
type tokenCache string
|
|
||||||
|
|
||||||
// Get the token from the config file - returns an error if it isn't present
|
|
||||||
func (name tokenCache) Token() (*oauth.Token, error) {
|
|
||||||
tokenString, err := fs.ConfigFile.GetValue(string(name), "token")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tokenString == "" {
|
|
||||||
return nil, fmt.Errorf("Empty token found - please reconfigure")
|
|
||||||
}
|
|
||||||
token := new(oauth.Token)
|
|
||||||
err = json.Unmarshal([]byte(tokenString), token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the token to the config file
|
|
||||||
//
|
|
||||||
// This saves the config file if it changes
|
|
||||||
func (name tokenCache) PutToken(token *oauth.Token) error {
|
|
||||||
tokenBytes, err := json.Marshal(token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tokenString := string(tokenBytes)
|
|
||||||
old := fs.ConfigFile.MustValue(string(name), "token")
|
|
||||||
if tokenString != old {
|
|
||||||
fs.ConfigFile.SetValue(string(name), "token", tokenString)
|
|
||||||
fs.SaveConfig()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FsDrive represents a remote drive server
|
// FsDrive represents a remote drive server
|
||||||
type FsDrive struct {
|
type FsDrive struct {
|
||||||
svc *drive.Service // the connection to the drive server
|
svc *drive.Service // the connection to the drive server
|
||||||
@ -268,39 +204,9 @@ OUTER:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes a new drive transport from the config
|
|
||||||
func newDriveTransport(name string) (*oauth.Transport, error) {
|
|
||||||
clientId := fs.ConfigFile.MustValue(name, "client_id")
|
|
||||||
if clientId == "" {
|
|
||||||
clientId = rcloneClientId
|
|
||||||
}
|
|
||||||
clientSecret := fs.ConfigFile.MustValue(name, "client_secret")
|
|
||||||
if clientSecret == "" {
|
|
||||||
clientSecret = rcloneClientSecret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings for authorization.
|
|
||||||
var driveConfig = &oauth.Config{
|
|
||||||
ClientId: clientId,
|
|
||||||
ClientSecret: clientSecret,
|
|
||||||
Scope: "https://www.googleapis.com/auth/drive",
|
|
||||||
RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
|
||||||
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
|
||||||
TokenCache: tokenCache(name),
|
|
||||||
}
|
|
||||||
|
|
||||||
t := &oauth.Transport{
|
|
||||||
Config: driveConfig,
|
|
||||||
Transport: http.DefaultTransport,
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFs contstructs an FsDrive from the path, container:path
|
// NewFs contstructs an FsDrive from the path, container:path
|
||||||
func NewFs(name, path string) (fs.Fs, error) {
|
func NewFs(name, path string) (fs.Fs, error) {
|
||||||
t, err := newDriveTransport(name)
|
t, err := driveAuth.NewTransport(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -309,18 +215,12 @@ func NewFs(name, path string) (fs.Fs, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &FsDrive{
|
f := &FsDrive{
|
||||||
root: root,
|
root: root,
|
||||||
dirCache: newDirCache(),
|
dirCache: newDirCache(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to pull the token from the cache; if this fails, we need to get one.
|
|
||||||
token, err := t.Config.TokenCache.Token()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to get token: %s", err)
|
|
||||||
}
|
|
||||||
t.Token = token
|
|
||||||
|
|
||||||
// Create a new authorized Drive client.
|
// Create a new authorized Drive client.
|
||||||
f.client = t.Client()
|
f.client = t.Client()
|
||||||
f.svc, err = drive.New(f.client)
|
f.svc, err = drive.New(f.client)
|
||||||
|
138
googleauth/googleauth.go
Normal file
138
googleauth/googleauth.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// Common authentication between Google Drive and Google Cloud Storage
|
||||||
|
package googleauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.google.com/p/goauth2/oauth"
|
||||||
|
"github.com/ncw/rclone/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A token cache to save the token in the config file section named
|
||||||
|
type TokenCache string
|
||||||
|
|
||||||
|
// Get the token from the config file - returns an error if it isn't present
|
||||||
|
func (name TokenCache) Token() (*oauth.Token, error) {
|
||||||
|
tokenString, err := fs.ConfigFile.GetValue(string(name), "token")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tokenString == "" {
|
||||||
|
return nil, fmt.Errorf("Empty token found - please reconfigure")
|
||||||
|
}
|
||||||
|
token := new(oauth.Token)
|
||||||
|
err = json.Unmarshal([]byte(tokenString), token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the token to the config file
|
||||||
|
//
|
||||||
|
// This saves the config file if it changes
|
||||||
|
func (name TokenCache) PutToken(token *oauth.Token) error {
|
||||||
|
tokenBytes, err := json.Marshal(token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tokenString := string(tokenBytes)
|
||||||
|
old := fs.ConfigFile.MustValue(string(name), "token")
|
||||||
|
if tokenString != old {
|
||||||
|
fs.ConfigFile.SetValue(string(name), "token", tokenString)
|
||||||
|
fs.SaveConfig()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth contains information to authenticate an app against google services
|
||||||
|
type Auth struct {
|
||||||
|
Scope string
|
||||||
|
DefaultClientId string
|
||||||
|
DefaultClientSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a new transport using authorisation from the config
|
||||||
|
//
|
||||||
|
// Doesn't have a token yet
|
||||||
|
func (auth *Auth) newTransport(name string) (*oauth.Transport, error) {
|
||||||
|
clientId := fs.ConfigFile.MustValue(name, "client_id")
|
||||||
|
if clientId == "" {
|
||||||
|
clientId = auth.DefaultClientId
|
||||||
|
}
|
||||||
|
clientSecret := fs.ConfigFile.MustValue(name, "client_secret")
|
||||||
|
if clientSecret == "" {
|
||||||
|
clientSecret = auth.DefaultClientSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings for authorization.
|
||||||
|
var config = &oauth.Config{
|
||||||
|
ClientId: clientId,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
Scope: auth.Scope,
|
||||||
|
RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
||||||
|
TokenCache: TokenCache(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &oauth.Transport{
|
||||||
|
Config: config,
|
||||||
|
Transport: http.DefaultTransport,
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a new transport using authorisation from the config with token
|
||||||
|
func (auth *Auth) NewTransport(name string) (*oauth.Transport, error) {
|
||||||
|
t, err := auth.newTransport(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to pull the token from the cache; if this fails, we need to get one.
|
||||||
|
token, err := t.Config.TokenCache.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to get token: %s", err)
|
||||||
|
}
|
||||||
|
t.Token = token
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration helper - called after the user has put in the defaults
|
||||||
|
func (auth *Auth) Config(name string) {
|
||||||
|
// See if already have a token
|
||||||
|
tokenString := fs.ConfigFile.MustValue(name, "token")
|
||||||
|
if tokenString != "" {
|
||||||
|
fmt.Printf("Already have a token - refresh?\n")
|
||||||
|
if !fs.Confirm() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a transport
|
||||||
|
t, err := auth.newTransport(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Couldn't make transport: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a URL for the user to visit for authorization.
|
||||||
|
authUrl := t.Config.AuthCodeURL("state")
|
||||||
|
fmt.Printf("Go to the following link in your browser\n")
|
||||||
|
fmt.Printf("%s\n", authUrl)
|
||||||
|
fmt.Printf("Log in, then type paste the token that is returned in the browser here\n")
|
||||||
|
|
||||||
|
// Read the code, and exchange it for a token.
|
||||||
|
fmt.Printf("Enter verification code> ")
|
||||||
|
authCode := fs.ReadLine()
|
||||||
|
_, err = t.Exchange(authCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to get token: %v", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user