mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 00:13:49 +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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@ -28,9 +27,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/goauth2/oauth"
|
||||
"code.google.com/p/google-api-go-client/drive/v2"
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/googleauth"
|
||||
"github.com/ogier/pflag"
|
||||
)
|
||||
|
||||
@ -47,14 +46,22 @@ const (
|
||||
var (
|
||||
// Flags
|
||||
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
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
Name: "drive",
|
||||
NewFs: NewFs,
|
||||
Config: Config,
|
||||
Name: "drive",
|
||||
NewFs: NewFs,
|
||||
Config: func(name string) {
|
||||
driveAuth.Config(name)
|
||||
},
|
||||
Options: []fs.Option{{
|
||||
Name: "client_id",
|
||||
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
|
||||
type FsDrive struct {
|
||||
svc *drive.Service // the connection to the drive server
|
||||
@ -268,39 +204,9 @@ OUTER:
|
||||
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
|
||||
func NewFs(name, path string) (fs.Fs, error) {
|
||||
t, err := newDriveTransport(name)
|
||||
t, err := driveAuth.NewTransport(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -309,18 +215,12 @@ func NewFs(name, path string) (fs.Fs, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := &FsDrive{
|
||||
root: root,
|
||||
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.
|
||||
f.client = t.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