diff --git a/backends/auth.go b/backends/auth.go index 386e935..f78feb3 100644 --- a/backends/auth.go +++ b/backends/auth.go @@ -23,7 +23,7 @@ import ( "github.com/spf13/viper" "go.uploadedlobster.com/scotty/models" - "golang.org/x/oauth2" + "go.uploadedlobster.com/scotty/storage" ) func BuildRedirectURL(config *viper.Viper, backend string) (*url.URL, error) { @@ -35,15 +35,20 @@ func BuildRedirectURL(config *viper.Viper, backend string) (*url.URL, error) { return url.Parse("http://" + callbackHost + callbackPath) } -func Authenticate(backend models.Backend, token *oauth2.Token, config *viper.Viper) (bool, error) { +func Authenticate(service string, backend models.Backend, db storage.Database, config *viper.Viper) (bool, error) { authenticator, auth := backend.(models.OAuth2Authenticator) if auth { - // FIXME redirectURL, err := BuildRedirectURL(config, backend.Name()) if err != nil { return auth, err } - authenticator.OAuth2Setup(redirectURL, token) + token, err := db.GetOAuth2Token(service) + if err != nil { + return auth, err + } + conf := authenticator.OAuth2Config(redirectURL) + tokenSource := NewDatabaseTokenSource(db, service, &conf, token) + authenticator.OAuth2Setup(tokenSource) } return auth, nil } diff --git a/backends/spotify/client.go b/backends/spotify/client.go index 6e51774..a164dcc 100644 --- a/backends/spotify/client.go +++ b/backends/spotify/client.go @@ -39,9 +39,9 @@ type Client struct { HttpClient *resty.Client } -func NewClient(conf oauth2.Config, token *oauth2.Token) Client { +func NewClient(token oauth2.TokenSource) Client { ctx := context.Background() - httpClient := conf.Client(ctx, token) + httpClient := oauth2.NewClient(ctx, token) client := resty.NewWithClient(httpClient) client.SetBaseURL(baseURL) client.SetHeader("Accept", "application/json") diff --git a/backends/spotify/client_test.go b/backends/spotify/client_test.go index 780830c..bdd1a6e 100644 --- a/backends/spotify/client_test.go +++ b/backends/spotify/client_test.go @@ -34,16 +34,15 @@ import ( ) func TestNewClient(t *testing.T) { - conf := oauth2.Config{} - token := &oauth2.Token{} - client := spotify.NewClient(conf, token) + token := oauth2.StaticTokenSource(&oauth2.Token{}) + client := spotify.NewClient(token) assert.IsType(t, spotify.Client{}, client) } func TestRecentlyPlayedAfter(t *testing.T) { defer httpmock.DeactivateAndReset() - client := spotify.NewClient(oauth2.Config{}, nil) + client := spotify.NewClient(nil) setupHttpMock(t, client.HttpClient.GetClient(), "https://api.spotify.com/v1/me/player/recently-played", "testdata/recently-played.json") @@ -63,7 +62,7 @@ func TestRecentlyPlayedAfter(t *testing.T) { func TestGetUserTracks(t *testing.T) { defer httpmock.DeactivateAndReset() - client := spotify.NewClient(oauth2.Config{}, nil) + client := spotify.NewClient(nil) setupHttpMock(t, client.HttpClient.GetClient(), "https://api.spotify.com/v1/me/tracks", "testdata/user-tracks.json") diff --git a/backends/spotify/spotify.go b/backends/spotify/spotify.go index 426e2af..0362d30 100644 --- a/backends/spotify/spotify.go +++ b/backends/spotify/spotify.go @@ -59,9 +59,8 @@ func (b *SpotifyApiBackend) OAuth2Config(redirectUrl *url.URL) oauth2.Config { } } -func (b *SpotifyApiBackend) OAuth2Setup(redirectUrl *url.URL, token *oauth2.Token) error { - config := b.OAuth2Config(redirectUrl) - b.client = NewClient(config, token) +func (b *SpotifyApiBackend) OAuth2Setup(token oauth2.TokenSource) error { + b.client = NewClient(token) return nil } diff --git a/backends/tokensource.go b/backends/tokensource.go new file mode 100644 index 0000000..a3e4970 --- /dev/null +++ b/backends/tokensource.go @@ -0,0 +1,70 @@ +/* +Copyright © 2023 Philipp Wolfer + +Scotty is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later version. + +Scotty is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +Scotty. If not, see . +*/ + +package backends + +import ( + "context" + + "go.uploadedlobster.com/scotty/storage" + "golang.org/x/oauth2" +) + +type databaseTokenSource struct { + new oauth2.TokenSource + db storage.Database + service string + tok *oauth2.Token +} + +func (s *databaseTokenSource) Token() (tok *oauth2.Token, err error) { + tok = s.tok + if tok == nil { + tok, _ = s.loadToken() + } + + if tok != nil && tok.Valid() { + return tok, nil + } + + if tok, err = s.new.Token(); err != nil { + return nil, err + } + + err = s.saveToken(tok) + s.tok = tok + if err != nil { + return nil, err + } + + return tok, err +} + +func (c *databaseTokenSource) saveToken(tok *oauth2.Token) error { + return c.db.SetOAuth2Token(c.service, tok) +} + +func (c *databaseTokenSource) loadToken() (*oauth2.Token, error) { + return c.db.GetOAuth2Token(c.service) +} + +func NewDatabaseTokenSource(db storage.Database, service string, config *oauth2.Config, tok *oauth2.Token) oauth2.TokenSource { + return &databaseTokenSource{ + db: db, + new: config.TokenSource(context.Background(), tok), + service: service, + tok: tok, + } +} diff --git a/cmd/listens.go b/cmd/listens.go index 5defd9d..a7a9287 100644 --- a/cmd/listens.go +++ b/cmd/listens.go @@ -49,21 +49,11 @@ var listensCmd = &cobra.Command{ cobra.CheckErr(err) // Authenticate backends, if needed - token, err := db.GetOAuth2Token(sourceName) + _, err = backends.Authenticate(sourceName, exportBackend, db, viper.GetViper()) cobra.CheckErr(err) - auth, err := backends.Authenticate(exportBackend, token, viper.GetViper()) - cobra.CheckErr(err) - if auth { - defer db.SetOAuth2Token(sourceName, token) - } - token, err = db.GetOAuth2Token(targetName) + _, err = backends.Authenticate(targetName, importBackend, db, viper.GetViper()) cobra.CheckErr(err) - auth, err = backends.Authenticate(importBackend, token, viper.GetViper()) - cobra.CheckErr(err) - if auth { - defer db.SetOAuth2Token(targetName, token) - } // Read timestamp timestamp := time.Unix(getInt64FromFlag(cmd, "timestamp"), 0) diff --git a/cmd/loves.go b/cmd/loves.go index 7c8bd54..e8759d8 100644 --- a/cmd/loves.go +++ b/cmd/loves.go @@ -49,21 +49,11 @@ var lovesCmd = &cobra.Command{ cobra.CheckErr(err) // Authenticate backends, if needed - token, err := db.GetOAuth2Token(sourceName) + _, err = backends.Authenticate(sourceName, exportBackend, db, viper.GetViper()) cobra.CheckErr(err) - auth, err := backends.Authenticate(exportBackend, token, viper.GetViper()) - cobra.CheckErr(err) - if auth { - db.SetOAuth2Token(sourceName, token) - } - token, err = db.GetOAuth2Token(targetName) + _, err = backends.Authenticate(targetName, importBackend, db, viper.GetViper()) cobra.CheckErr(err) - auth, err = backends.Authenticate(importBackend, token, viper.GetViper()) - cobra.CheckErr(err) - if auth { - defer db.SetOAuth2Token(targetName, token) - } // Read timestamp timestamp := time.Unix(getInt64FromFlag(cmd, "timestamp"), 0) diff --git a/models/interfaces.go b/models/interfaces.go index b676ba6..7928804 100644 --- a/models/interfaces.go +++ b/models/interfaces.go @@ -90,5 +90,5 @@ type OAuth2Authenticator interface { OAuth2Config(redirectUrl *url.URL) oauth2.Config // Setup the OAuth2 client - OAuth2Setup(redirectUrl *url.URL, token *oauth2.Token) error + OAuth2Setup(token oauth2.TokenSource) error }