mirror of
https://github.com/rclone/rclone.git
synced 2024-12-01 04:45:03 +01:00
139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
|
// 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)
|
||
|
}
|
||
|
}
|